单源最短路径问题(Java)

news2025/1/19 7:07:19

单源最短路径问题(Java)

文章目录

  • 单源最短路径问题(Java)
    • 1、问题描述
    • 2、算法思路
    • 3、代码实现
    • 4、算法正确性和计算复杂性
      • 4.1 贪心选择性质
      • 4.2 最优子结构性质
      • 4.3 计算复杂性
    • 5、参考资料


在这里插入图片描述


1、问题描述

给定带权有向图G=(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点, 称为。现在要计算从源到所有其他各顶点的最短路长度。这里路的长度是指路上各边权之和。这个问题通常称为单源最短路径问题

其中,V表示顶点集合,E表示各个节点之间的边。

2、算法思路

对于单源最短路径问题,Dijkstra算法是解决这个问题的贪心算法。

基本思想

设置顶点集合S并不断地做贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。

初始时,S 中仅含有源。设u是G 的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u 的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度

Dijkstra 算法每次从v-s中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist 进行必要的修改。一旦S包含了所有V中顶点,dist数组就记录了从源到所有其他顶点之间的最短路径长度。

Dijkstra 算法可描述如下。

其中, 输入的带权有向图是G = (V, E) , V = {1 , 2, …, n} 。顶点v是源。a是一个二维数组,a[i][j]表示边(i,j)的权。当(i, j) 时,a[i][j]是一个大数。如dist[i]表示当前从源到顶点t的最短特殊路径长度。

3、代码实现

例如,对下图中的有向图,应用Dijkstra算法计算从源顶点1到其它顶点间最短路径的过程列在下页的表中。

题目示意图

在这里插入图片描述

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

/**
 * TODO  1 --> 4 --> 3 --> 5
 */
public class Solution {

    private static int vNum, eNum;
    private static int[] v;
    private static float[][] e;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("input the number of vertix and edge:");
        vNum = scanner.nextInt();
        eNum = scanner.nextInt();

        v = new int[vNum];
        e = new float[vNum + 1][vNum + 1];
        for (int i = 0; i < e.length; i++) {
            for (int j = 0; j < e.length; j++) {
                e[i][j] = Float.MAX_VALUE;
            }
        }

        System.out.print("input the vertix information:");
        for (int i = 0; i < v.length; i++) {
            v[i] = scanner.nextInt();
        }
        System.out.println("input the weight of edges:");
        for (int i = 0; i < eNum; i++) {
            int start = scanner.nextInt(), target = scanner.nextInt();
            e[start][target] = (float) scanner.nextInt();
        }

        System.out.print("顶点有:");
        for (int i = 0; i < v.length; i++) {
            if (i == v.length - 1) {
                System.out.println(v[i]);
            } else {
                System.out.print(v[i] + ", ");
            }
        }

        System.out.println("边与边之间的距离:");
        for (int i = 1; i < e.length; i++) {
            for (int j = 1; j < e.length; j++) {
                if (i != j && e[i][j] != Float.MAX_VALUE) {
                    System.out.print("[" + i + ", " + j + "] = " + e[i][j] + "; ");
                }
            }
        }
        System.out.println();

        int[] path = new int[vNum + 1];
        float[] dist = new float[vNum + 1];

        Dijkstra(v[0], e, dist, path);

        System.out.print("Dijkstra路径为:");
        List<Integer> list = new ArrayList<>();
        list.add(vNum);
        list.add(path[vNum]);
        while (true) {
            if (path[list.get(list.size() - 1)] == 1) {
                list.add(1);
                break;
            }
            list.add(path[list.get(list.size() - 1)]);
        }
        for (int j = list.size() - 1; j >= 0; j--) {
            if (j != 0) {
                System.out.print(list.get(j) + "-->");
            } else {
                System.out.println(list.get(j));
            }
        }

        System.out.println("从顶点1到各顶点最短距离:");
        for (int i = 1; i < dist.length; i++) {
            System.out.println("dist[" + i + "] = " + dist[i]);
        }

    }

    public static void Dijkstra(int vertix, float[][] weight, float[] dist, int[] path) {
        int n = dist.length - 1;
        if (vertix < 1 || vertix > n + 1) {
            return;
        }
        boolean[] vis = new boolean[n + 2];
        // initialize
        for (int i = 1; i <= n; i++) {
            dist[i] = weight[vertix][i];
            vis[i] = false;
            if (dist[i] == Float.MAX_VALUE) {
                path[i] = 0;
            } else {
                path[i] = vertix;
            }
        }
        dist[vertix] = 0;
        vis[vertix] = true;
        for (int i = 1; i < n; i++) {
            float tmp = Float.MAX_VALUE;
            int u = vertix;
            for (int j = 1; j <= n; j++) {
                if ((!vis[j]) && (dist[j] < tmp)) {
                    u = j;
                    tmp = dist[j];
                }
            }
            vis[u] = true;
            for (int j = 1; j <= n; j++) {
                if ((!vis[j]) && (weight[u][j] < Float.MAX_VALUE)) {
                    float newDist = dist[u] + weight[u][j];
                    if (newDist < dist[j]) {
                        dist[j] = newDist;
                        path[j] = u;
                    }
                }
            }
        }
    }
}

运行结果

在这里插入图片描述

Dijkstra算法的迭代过程:

在这里插入图片描述
在这里插入图片描述

图解过程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4、算法正确性和计算复杂性

4.1 贪心选择性质

(1)根据算法可知,最短距离是最小的路长,故
dist[x]<= d(v,x)

(2)假设存在另外一条更短路红色线所示,故
d(v,x)+d(x,u)=d(v,u)<dist[u]

(3)由(1)(2)可知,dist[x]<dist[u]。此为矛盾,因为如果(3)成立,此时应该选择 x进入S集合,即选择具有最短特殊路径的顶点是x,而不是u。(因为根据最短路径算法,总是选取最短路径的顶点进入S)
在这里插入图片描述

4.2 最优子结构性质

该性质描述为:如果S(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么S(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

假设S(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有S(i,j)=S(i,k)+S(k,s)+S(s,j)。而S(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径S’(k,s),那么S’(i,j)=S(i,k)+S’(k,s)+S(s,j)<S(i,j)。则与S(i,j)是从i到j的最短路径相矛盾。因此该性质得证。

4.3 计算复杂性

对于具有n个顶点和e条边的带权有向图, 如果用带权邻接矩阵表示这个图,那么Dijkstra算法的主循环体需要O(n) 时间。这个循环需要执行n-1次,所以完成循环需要
0(n2)时间。算法的其余部分所需要时间不超过0(n2)。

5、参考资料

  • 算法设计与分析(第四版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/41289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式电源接入对配电网的影响matlab程序(IEEE9节点系统算例)

分布式电源接入对配电网的影响matlab程序&#xff08;IEEE9节点系统算例&#xff09; 摘 要&#xff1a;分布式电源的接入使得配电系统从放射状无源网络变为分布有中小型电源的有源网络。带来了使单向流动的电流方向具有了不确定性等等问题&#xff0c;使得配电系统的控制和管…

Android反编译apk

文章目录安装Android Studio1. 解压apk文件方法一&#xff1a;使用apktool反编译&#xff08;得到的是.smali文件和可直接读的资源文件&#xff0c;如果要得到.dex文件&#xff0c;还要看方法二&#xff09;方法二&#xff1a;使用解压工具解压&#xff08;得到的是.dex文件和二…

SpringBoot项目集成Dubbo

1.环境搭建 为整合Dubbo之前&#xff0c;我们所写的项目都是单一应用架构&#xff0c;只需要一个应用&#xff0c;将所有功能都部署在一起&#xff0c;在应用内部是控制层调用业务层&#xff0c;业务层调用数据持久层&#xff1b;如今&#xff0c;整合Dubbo后&#xff0c;我们…

独立产品灵感周刊 DecoHack #039 - 制作自己的音乐墙

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。自荐产品 1. planet-tab - 由独立开发者 ha…

【云原生】Docker的私有仓库部署——Harbor

内容预知 1.Docker原生私有仓库—— Registry 1.1 Registry的简单了解 1.2 Registry的部署过程 步骤一&#xff1a;拉取相关的镜像 步骤二&#xff1a;进行 Registry的相关yml文件配置&#xff08;docker-compose&#xff09; 步骤三&#xff1a;镜像的推送 2. Registry的…

SpringBoot SpringBoot 原理篇 2 自定义starter 2.6 拦截器开发

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇2 自定义starter2.6 拦截器开发2.6.1 拦截器开发2.6.2 小结2 自定义starter …

2022年11月27日学习 SVM

SVM&#xff0c;英文全称为 Support Vector Machine&#xff0c;中文名为支持向量机 ​ SVM也是一种分类算法&#xff0c;它的核心思想用我自己的话来讲就是先找到两个类别中距离最近的几个点作为支持向量&#xff0c;然后计算超平面&#xff0c;超平面需要间隔最大化。然后用超…

【Hack The Box】linux练习-- Previse

HTB 学习笔记 【Hack The Box】linux练习-- Previse &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x1f…

Microsoft SQL Server中的错误配置

介绍 这篇文章将介绍如何利用Microsoft SQL Server中的错误配置&#xff0c;尝试获取反向shell并熟悉Impacket工具的使用&#xff0c;以便进一步攻击某些服务。 impacket的安装地址&#xff1a;https://github.com/SecureAuthCorp/impacket Impacket是用于处理网络协议的Pyt…

FPGA学习-vivado软件的使用

FPGA学习-vivado软件的使用1.杂谈2. vivado新建工程1.杂谈 又被封了7天。 正好封控前领导让我改下fpga代码&#xff0c;趁这个机会好好学习下&#xff0c;虽然在这块一片空白&#xff0c;但是毕竟这块是我的短板&#xff0c;一个不会写代码的硬件工程师是一个不完整的硬件工程…

无条码商品新建商品档案,搭配蓝牙便携打印机移动打印条码标签

null无条码商品的商品档案新建&#xff0c;并打印条码标签&#xff0c;即可实现仓库条码管理&#xff0c;扫码入库&#xff0c;出库&#xff0c;盘点等操作。, 视频播放量 1、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 0、转发人数 0, 视频作者 汉码盘点机PDA, 作者简介 &am…

【WSN通信】A_Star改进LEACH多跳传输协议【含Matlab源码 487期】

⛄一、 A_Star改进LEACH多跳传输协议简介 1 理论基础 1.1 A*算法 A算法是一种智能搜索算法,他在求解问题时所得到的结果会选择所有路径中代价最小的节点。 A算法是一种基于启发式函数的算法,搜索过程如下:首先创建两个分别命名为open表和close表的表格,其中open表中存放还未访…

【Hack The Box】linux练习-- Doctor

HTB 学习笔记 【Hack The Box】linux练习-- Doctor &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月23日&#x1f334; &#x1f3…

量子计算(九):复合系统与联合测量

文章目录 复合系统与联合测量 一、张量积 二、复合系统的状态演化 复合系统与联合测量 拥有两个或两个以上的量子比特的量子系统通常被称为复合系统&#xff08;composite systems&#xff09;。单量子比特系统的描述与测量已有所了解&#xff0c;那么多个量子比特的系统该如…

R语言基于ARMA-GARCH过程的VaR拟合和预测

本文展示了如何基于基础ARMA-GARCH过程&#xff08;当然这也涉及广义上的QRM&#xff09;来拟合和预测风险价值&#xff08;Value-at-Risk&#xff0c;VaR&#xff09;。 最近我们被客户要求撰写关于ARMA-GARCH的研究报告&#xff0c;包括一些图形和统计输出。 视频&#xff…

树莓派安装ubuntu系统

ubuntu镜像下载 官方地址&#xff1a;https://cn.ubuntu.com/download/raspberry-pi 清华镜像&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu/releases/22.04.1/release/ 准备树莓派镜像工具&#xff08;Raspberry Pi Imager&#xff09; 下载地…

如何在 Spring Boot 中使用 JPA 和 JPQL 进行自定义查询示例

在本教程中&#xff0c;您将了解如何在 Spring Boot 示例中使用 Spring JPA Query进行自定义查询。我将向您展示&#xff1a; 使用JPQL&#xff08;Java持久性查询语言&#xff09;的方法如何在 Spring 引导中执行 SQL 查询具有 WHERE 条件的 JPA 选择查询示例内容 JPQL 与本机…

【408专项篇】C语言笔记-第七章(函数)

第一节&#xff1a;函数的声明与定定义 1. 函数的声明与定义 函数间的调用关系是&#xff1a;由主函数调用其他函数&#xff0c;其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意次。 #include "func.h"int main() {int a10;aprint_star(a);print_…

SPI和API还在傻傻分不清楚?

什么是SPI 介绍 再聊下一个类加载器框架OSGI之前&#xff0c;我们首先学习一下前驱知识SPI 全称&#xff1a;Service Provider Interface 区别于API模式&#xff0c;本质是一种服务接口规范定义权的转移&#xff0c;从服务提供者转移到服务消费者。 怎么理解呢&#xff1f…

JavaScript游戏开发(3)(笔记)

文章目录七、支持移动设备的横向卷轴游戏准备7.1 角色的简单移动7.2 背景7.3 加入敌人与帧数控制7.4 碰撞、计分、重新开始7.5 手机格式7.6 全屏模式7.7 存在的问题附录素材可以去一位大佬放在github的源码中直接下&#xff0c;见附录。 七、支持移动设备的横向卷轴游戏 使用…