参考:
https://blog.csdn.net/heroacool/article/details/51014824
图:
基本思想
-
通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
-
此外,引进两个集合T和U。T的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
-
初始时,T中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是”起点s到该顶点的路径”。然后,从U中找出路径最短的顶点,并将其加入到T中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到T中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。
操作步骤
-
初始时,T只包含起点s;U包含除s外的其他顶点,且U中顶点的距离∞。基于邻接图根据s更新U中各个顶点到s的距离;若(s,v)的距离小于(s,k)+(k,v)的距离,即更新s的最优路径;
-
从U中选出”距离最短的顶点k”,并将顶点k加入到T中;同时,从U中移除顶点k。
-
基于邻接图根据顶点k更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;若(s,v)的距离小于(s,k)+(k,v)的距离,即更新s的最优路径;
-
重复步骤(2)和(3),直到遍历完所有顶点。
图解
以上图G4为例,来对迪杰斯特拉进行算法演示(以第4个顶点D为起点)。以下B节点中23应为13。
初始状态:S是已计算出最短路径的顶点集合,U是未计算除最短路径的顶点的集合!
第1步:将顶点D加入到S中。
此时,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起点D的距离是3。
第2步:将顶点C加入到S中。
上一步操作之后,U中顶点C到起点D的距离最短;因此,将C加入到S中,同时更新U中顶点的距离。以顶点F为例,之前F到D的距离为∞;但是将C加入到S之后,F到D的距离为9=(F,C)+(C,D)。
此时,S={D(0),C(3)}, U={A(∞),B(23),E(4),F(9),G(∞)}。
第3步:将顶点E加入到S中。
上一步操作之后,U中顶点E到起点D的距离最短;因此,将E加入到S中,同时更新U中顶点的距离。还是以顶点F为例,之前F到D的距离为9;但是将E加入到S之后,F到D的距离为6=(F,E)+(E,D)。
此时,S={D(0),C(3),E(4)}, U={A(∞),B(23),F(6),G(12)}。
第4步:将顶点F加入到S中。
此时,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。
第5步:将顶点G加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。
第6步:将顶点B加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。
第7步:将顶点A加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。
此时,起点D到各个顶点的最短距离就计算出来了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。
代码实现(Java)
import java.util.LinkedList;
import java.util.List;
public class Main {
final static int INF = 1000;
public static void main(String[] args) {
int[][] a = { { INF, 12, INF, INF, INF, 16, 14 },
{ 12 , INF, 10, INF, INF, 7, INF },
{ INF, 10, INF, 3, 5, 6, INF },
{ INF, INF, 3, INF, 4, INF, INF },
{ INF, INF, 5, 4, INF, 2, 8 },
{ 16, 7, 6, INF, 2, INF, 9 },
{ 14, INF, INF, INF, 8, 9, INF }
};
List<Node> T = new LinkedList<>();//已经成功找到最小点的集合
List<Node> U = new LinkedList<>();//还没成功找到最小点的集合
//将起始点加入T
T.add(new Node(0,0));
//其他点的初始权重设为无穷大,并相对于七十点根据邻接矩阵进行更新
for (int i = 1; i < a[0].length; i++) {
U.add(new Node(INF,i));
}
updataU(a,T.get(0),U);//更新路径
/*核心算法
* 1、找到U中目前权重最小的点indexMin
* 2、将indexMin从U转入T
* 3、更新U中节点的权重
*/
for (int i = 1; i < a.length; i++) {
int indexMin = findMin(U);
Node nodeTemp = U.get(indexMin);
//nodeTemp.path.add(0);
T.add(nodeTemp);
U.remove(indexMin);
updataU(a,nodeTemp,U);
}
//输出
for (int i = 0; i < T.size(); i++) {
System.out.print(i+1 +" "+ T.get(i).weight+" ");
System.out.println(path(T.get(i)));
}
}
/**
* 求node节点的最优路径
* @param node
* @return 若为起始点,返回0;否则,返回上一节点路径+当前节点
*/
private static String path(Node node) {
if(node.last != null)
return path(node.last) +"->"+ node.index;
else
return "0";
}
/**
* 基于a根据nodeTemp更新u
* @param a 邻接矩阵
* @param nodeTemp 新加入的节点
* @param u 待更新的U
* 如果经由nodeTemp到达当前节点的路径的权重更小,则跟新当前节点权重,并更新当前节点的前置节点
*/
private static void updataU(int[][] a, Node nodeTemp, List<Node> u) {
for (int i = 0; i < u.size(); i++) {
if(a[nodeTemp.index][u.get(i).index]+nodeTemp.weight < u.get(i).weight) {
u.get(i).weight = a[nodeTemp.index][u.get(i).index]+nodeTemp.weight;
u.get(i).last = nodeTemp;
}
}
}
/**
* 根据u中元素的路径权重找出权重最小的元素
* @param u
* @return u中权重最小的元素的索引
*/
private static int findMin(List<Node> u) {
int t = 0;
for (int i = 1; i < u.size(); i++) {
if(u.get(i).weight < u.get(t).weight)
t = i;
}
return t;
}
/**
* 节点类
* @author alixia
* index: 节点索引
* weight: 最优路径权重
* last: 最优路径上的上一节点
*/
private static class Node{
public int index;
public int weight;
public Node last;
public Node(int weight, int index) {
this.weight = weight;
this.index = index;
this.last = null;
}
}
}