加权图的最短路径算法发明者是Edsger Dijkstra,一个荷兰的计算机科学家。我之前写的关于栈的文章里介绍过这个科学家,因为双栈算法也是他发明的。这个算法的核心是两个集合。第一个集合是路径长度已经确定的集合。加权图的最短路径,计算的是到每个点的最短路径。所以它的输入参数只有起点。它的核心思想超级简单啊。对于N的点的图来说,起点为S。那么距离最短的点肯定是S本身。这个算法维护了一个数组,叫做距离数组。数组下标对应点的索引。
初始化时距离数组里只有S本身的距离是0。其余点都是正无穷。
第一轮
遍历S的所有边,将他们到S的距离更新为边的权重。
第二轮才是有意思的地方。
找到距离S最近的点U
遍历U的所有边,以V代替,将S到U的距离
d
[
u
]
d[u]
d[u]加上U到V的距离
w
(
u
,
v
)
w(u,v)
w(u,v)。
这个距离如果小于S到V的距离
d
[
v
]
d[v]
d[v],那么修正S到V的距离为新距离,也就是修正距离数组。
核心算法就是不断修正这个距离,最后计算出S到各个点的最短路径。我用一张图表示一下:
为了避免成环,把所有的U放入一个哈希集合中,每次选择U时,都选择哈希集合中不存在的,或者说是已经计算过的。
这个算法实际上是遍历了所有的边,并且修正了所有的距离来计算出加权图的最短路径。代码如下:
/**
* 最短路径
* @return 列表
*/
public int[] shortestPath(int source) {
// 首先一个set T
final HashSet<Integer> t = new HashSet<>();
// cost是一个数组啊
int[] cost = new int[vertices.length];
Arrays.fill(cost, Integer.MAX_VALUE);
cost[source] = 0;
int[] parent = new int[vertices.length];
parent[source] = -1;
while (t.size() < vertices.length) {
// u 要是最短的 u not in t with the smallest cost
int u = findU(t, cost);
t.add(u);
for(WeightedEdge edge : edges[u]) {
final int v = edge.getTo();
if (cost[v] > cost[u] + edge.getWeight()) {
cost[v] = cost[u] + edge.getWeight();
parent[v] = u;
}
}
}
return parent;
}
private int findU(HashSet<Integer> t, int[] cost) {
final ArrayList<Integer> temp = new ArrayList<>();
for (int i = 0; i < vertices.length; i++) {
if (!t.contains(i)) {
temp.add(i);
}
}
int u = temp.get(0);
// 找出最小的索引
for (int i = 1; i < temp.size(); i++) {
if (cost[temp.get(i)] < cost[u]) {
u = temp.get(i);
}
}
return u;
}