一、基于邻接表的Dijkstra算法

  如前一篇文章所述,在 Dijkstra 的算法中,维护了两组,一组包含已经包含在最短路径树中的顶点列表,另一组包含尚未包含的顶点。使用邻接表表示,可以使用 BFS 在O(V + E)时间中遍历图的所有顶点  。这个想法是使用 BFS 遍历图的所有顶点,并使用最小堆存储尚未包括在最短路径树中的顶点(或尚未确定最短距离的顶点)。最小堆用作优先级队列,以从尚未包括的顶点集中获取最小距离顶点。对于Min Heap,诸如 extract-min 和 reduce-key 值之类的操作的时间复杂度为 O(logV)。使用邻接表表示的 Dijkstra算法时间复杂度为 O(ELogV)。

二、详细步骤

  1) 创建大小为 V 的最小堆,其中 V 是给定图中的顶点数。最小堆的每个节点都包含顶点数顶点的距离值
  2) 以源顶点为根初始化 Min Heap(分配给源顶点的距离值为 0)。分配给所有其他顶点的距离值为 INF(无穷大)。
  3) 当“最小堆”不为空时,执行以下操作:

    • 从“最小堆”中提取具有最小距离值节点的顶点。让提取的顶点为u。
    • 对于u的每个相邻顶点v,检查v是否在Min Heap中。如果 v 在“最小堆”中,并且距离值大于uv 的权重加上 u 的距离值,则更新 v 的距离值。

  用下面的例子来理解。让给定的源顶点为 0:

  最初,源顶点到达自身的距离值为 0,对于所有其他顶点,INF 为无穷大。因此,从“最小堆”中提取源顶点,并更新与 0(1和7)相邻的顶点的距离值。“最小堆”包含除顶点 0 以外的所有顶点。绿色的顶点是确定了最小距离的顶点,并且不在“最小堆”中。

  由于顶点1 的距离值在最小堆中的所有节点中最小,因此从最小堆中提取顶点,并更新与 1 相邻的顶点的距离值(如果顶点不在最小堆中且距离 1 的距离较短,则更新距离比之前的距离)。最小堆包含除顶点0 和 1 以外的所有顶点。

  从最小堆中选取最小距离值的顶点。选择了顶点7。因此,最小堆现在包含除 0、1 和 7 以外的所有顶点。更新相邻 顶点7 的距离值。顶点6 和 8 的距离值变得有限(分别为15和9)。

  选择与最小堆的距离最小的顶点。选择了顶点6。因此,最小堆现在包含除 0、1、7 和 6 以外的所有顶点。更新相邻顶点6的距离值。更新顶点5 和 8 的距离值。

  重复上述步骤,直到最小堆为空为止。最后,我们得到以下最短路径树。

三、代码

  下面是使用了邻接矩阵的迪杰斯特拉算法实现。

 1     /**
2 * 使用邻接表来实现Dijkstra的单源最短路径算法的函数
3 *
4 * @param adj 邻接表
5 * @param src 源顶点
6 */
7 public void dijkstra(List<List<Node>> adj, int src) {
8 this.adj = adj;
9 for (int i = 0; i < V; i++) {
10 dist[i] = Integer.MAX_VALUE;
11 }
12
13 /* 将源节点添加到优先级队列 */
14 pq.add(new Node(src, 0));
15
16 /* 源顶点与其自身的距离始终为0 */
17 dist[src] = 0;
18
19 while (settled.size() != V) {
20 int u = pq.remove().node;
21 settled.add(u);
22 e_Neighbours(u);
23 }
24 }

  处理传递的节点的所有邻居。

 1     /**
2 * 处理传递过来的节点的所有邻居
3 *
4 * @param u
5 */
6 private void e_Neighbours(int u) {
7 int edgeDistance = -1;
8 int newDistance = -1;
9
10 /* v的所有邻居 */
11 for (int i = 0; i < adj.get(u).size(); i++) {
12 Node v = adj.get(u).get(i);
13
14 /* 如果当前节点尚未处理 */
15 if (!settled.contains(v.node)) {
16 edgeDistance = v.cost;
17 newDistance = dist[u] + edgeDistance;
18
19 /* 如果新距离的成本更低 */
20 if (newDistance < dist[v.node])
21 dist[v.node] = newDistance;
22
23 /* 将当前节点添加到队列 */
24 pq.add(new Node(v.node, dist[v.node]));
25 }
26 }
27 }

 源代码:

  1 package algorithm.shortestpath;
2
3 import java.util.*;
4
5 public class DijkstraPQ {
6 private int[] dist; // 当前的距离数组
7 private Set<Integer> settled; // 存储最短路径处理完的顶点Set集合
8 private PriorityQueue<Node> pq; // 优先级队列(min-heap)
9 private int V; // 顶点数量
10 List<List<Node>> adj; // 邻接表
11
12 public DijkstraPQ(int v) {
13 this.V = v;
14 dist = new int[V];
15 settled = new HashSet<>();
16 pq = new PriorityQueue<>(V, new Node());
17 }
18
19 /**
20 * 使用邻接表来实现Dijkstra的单源最短路径算法的函数
21 *
22 * @param adj 邻接表
23 * @param src 源顶点
24 */
25 public void dijkstra(List<List<Node>> adj, int src) {
26 this.adj = adj;
27 for (int i = 0; i < V; i++) {
28 dist[i] = Integer.MAX_VALUE;
29 }
30
31 /* 将源节点添加到优先级队列 */
32 pq.add(new Node(src, 0));
33
34 /* 源顶点与其自身的距离始终为0 */
35 dist[src] = 0;
36
37 while (settled.size() != V) {
38 int u = pq.remove().node;
39 settled.add(u);
40 e_Neighbours(u);
41 }
42 }
43
44 /**
45 * 处理传递的节点的所有邻居的函数
46 *
47 * @param u
48 */
49 private void e_Neighbours(int u) {
50 int edgeDistance = -1;
51 int newDistance = -1;
52
53 /* v的所有邻居 */
54 for (int i = 0; i < adj.get(u).size(); i++) {
55 Node v = adj.get(u).get(i);
56
57 /* 如果当前节点尚未处理 */
58 if (!settled.contains(v.node)) {
59 edgeDistance = v.cost;
60 newDistance = dist[u] + edgeDistance;
61
62 /* 如果新距离的成本更低 */
63 if (newDistance < dist[v.node])
64 dist[v.node] = newDistance;
65
66 /* 更新后将当前节点添加到最小堆中 */
67 pq.add(new Node(v.node, dist[v.node]));
68 }
69 }
70 }
71
72 /**
73 * 测试主函数
74 *
75 * @param args
76 */
77 public static void main(String[] args) {
78 int V = 5;
79 int source = 0;
80 List<List<Node>> adj = new ArrayList<>();
81
82 for (int i = 0;i < V; i++) {
83 List<Node> item = new ArrayList<>();
84 adj.add(item);
85 }
86
87 // 邻接表的输入
88 adj.get(0).add(new Node(1, 9));
89 adj.get(0).add(new Node(2, 6));
90 adj.get(0).add(new Node(3, 5));
91 adj.get(0).add(new Node(4, 3));
92
93 adj.get(2).add(new Node(1, 2));
94 adj.get(2).add(new Node(3, 4));
95
96 DijkstraPQ dijkstraPQ = new DijkstraPQ(V);
97 dijkstraPQ.dijkstra(adj, source);
98
99 System.out.println("The shortest path form node: ");
100 for (int i = 0; i < dijkstraPQ.dist.length; i++) {
101 System.out.println(source + " to " + i + " is " + dijkstraPQ.dist[i]);
102 }
103 }
104 }
105
106 class Node implements Comparator<Node> {
107
108 public int node; // 顶点数
109 public int cost; // 顶点的距离值
110
111 public Node() {
112 }
113
114 public Node(int node, int cost) {
115 this.node = node;
116 this.cost = cost;
117 }
118
119 @Override
120 public int compare(Node o1, Node o2) {
121 if (o1.cost < o2.cost)
122 return -1;
123 if (o1.cost > o2.cost)
124 return 1;
125 return 0;
126 }
127 }

单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(二)的更多相关文章

  1. [C++]单源最短路径:迪杰斯特拉(Dijkstra)算法(贪心算法)

    1 Dijkstra算法 1.1 算法基本信息 解决问题/提出背景 单源最短路径(在带权有向图中,求从某顶点到其余各顶点的最短路径) 算法思想 贪心算法 按路径长度递增的次序,依次产生最短路径的算法 ...

  2. JS实现最短路径之迪杰斯特拉(Dijkstra)算法

    最短路径: 对于网图来说,最短路径是指两个顶点之间经过的边上权值和最少的路径,我们称第一个顶点是源点,最后一个顶点是终点 迪杰斯特拉 ( Dijkstra) 算法是并不是一下子就求出 了 Vo 到V8 ...

  3. 最短路径算法-迪杰斯特拉(Dijkstra)算法在c#中的实现和生产应用

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先遍历思想),直到扩展到终点为止 贪心算法(Greedy ...

  4. 迪杰斯特拉Dijkstra算法介绍

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijk ...

  5. 最短路径-迪杰斯特拉(dijkstra)算法及优化详解

    简介: dijkstra算法解决图论中源点到任意一点的最短路径. 算法思想: 算法特点: dijkstra算法解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树.该算法常用于路由算 ...

  6. 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)

    一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...

  7. 图的最短路径---迪杰斯特拉(Dijkstra)算法浅析

    什么是最短路径 在网图和非网图中,最短路径的含义是不一样的.对于非网图没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径. 对于网图,最短路径就是指两顶点之间经过的边上权值之和最 ...

  8. 最短路径 - 迪杰斯特拉(Dijkstra)算法

    对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点为源点,最后一个顶点为终点.最短路径的算法主要有迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd ...

  9. C# 迪杰斯特拉(Dijkstra)算法

    Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 其基本思想是,设置顶点集合S并不断地作 ...

随机推荐

  1. SpringMVC执行流程总结

    SpringMVC 执行流程: 用户发送请求至前端控制器 DispatcherServlet DispatcherServlet 收到请求调用处理映射器 HandlerMapping 处理映射器根据请 ...

  2. VMware ESXi 7.0 U2 SLIC 2.6 & Unlocker 集成 Intel NUC 网卡、USB 网卡和 NVMe 驱动

    ESXi 7 U2 标准版镜像集成 NUC 网卡.USB 网卡 和 NVMe 驱动. 请访问原文链接:https://sysin.org/blog/vmware-esxi-7-u2-nuc-usb-n ...

  3. PHP中的PDO对象操作学习(一)初始化PDO及原始SQL语句操作

    PDO 已经是 PHP 中操作数据库事实上的标准.包括现在的框架和各种类库,都是以 PDO 作为数据库的连接方式.基本上只有我们自己在写简单的测试代码或者小的功能时会使用 mysqli 来操作数据库. ...

  4. 停止:service jenkins stop,提示:Failed to stop jenkins.service: Unit jenkins.service not loaded.

    uni@uni-virtual-machine:~$ service jenkins stop Failed to stop jenkins.service: Unit jenkins.service ...

  5. FastAPI logger日志记录方案 loguru模块

    实现方式: 采用 loguru 模块.跟flask直接挂载到app上有区别,当然也可以尝试去这样做. 但是 好像没有这个必要.要的就是个快速.整那些子虚乌有的东西完全木有意义. 1.首先是去项目git ...

  6. Expression Tree 遍历集合

    场景 从接口返回的数据是集合,却是 object 类型的.这个时候需要遍历这个集合.现提供两种方法. 方法一: 因为集合是可枚举的,所以可以尝试转为 IEnumerable 类型,然后遍历即可. st ...

  7. CF1251F-Red-White Fence【NTT】

    前言 刚开始看错题推了半天的生成函数 正题 题目链接:https://www.luogu.com.cn/problem/CF1251F 题目大意 $n$个白色木板,$k$个红色木板,给出这些木板的高度 ...

  8. Java——多线程编程学习/01

    原文章:http://www.cnblogs.com/QG-whz/p/8351298.html  注:建议去看原博主的文章,单就这个知识点而论,比书本讲的透彻,如有违规,联系必删! 并发环境下进行编 ...

  9. vue项目中的element-ui地区级联选择器加详细地址push到对象中不显示的问题

    想要实现级联选择器el-cascader和输入框el-input共同组成的详细地址,添加数据时弹出el-drawer嵌套el-form弹窗,然后在el-form添加数据提交后push到el-table ...

  10. windows下如何查看所有端口及占用

    1.在windows下查看所有端口: 先点击电脑左下角的开始,然后选择运行选项,接着我们在弹出的窗口中,输入[cmd]命令,进行命令提示符. 然后我们在窗口中输入[netstat -ano]按下回车, ...