经典的算法,求两个城市间的最短距离。
治学之道,最忌知其然而不知其所以然。所以今天就让我们来一步一步了解图,最短路径算法Dijkstra
以下是来自经典算法书籍 § Algorithms 的图的Java表示:
Princeton Graph.java
Bag.java
以下是Dijkstra 算法的证明:
Dijkstra's Algorithm的证明
麻省理工大学算法公开课:Dijkstra 算法-麻省理工大学
其实看完最短路径算法第一节课就大概明白了Dijkstra算法的原理。
这里存在一个假设:我们不存在任何的负权边。如果有负权边,我们是不能使用Dijkstra算法的。因为它使用的是贪心算法。
证明: 维护一个已经是最短距离顶点的set1, 以及其它顶点的set.
每次搜索余下的所有的点,找到一个最短距离的顶点,将它加到set1。为什么它就是最短距离呢?
Proof:
假设找到的这个点是A点,假如如果我们可以从set1中到B点再到A点,因为dist[src][B] > dist[src][A],
所以dist[src][B] + dist[B][A] >= dist[src][A],所以经过B的路径还是比从set直接到A要远。因此我们找到的从原点到A的路径就是最短路径。
这样就可以把A点加入到set1中。
set1中加入A点后,我们要把与A相接的所有的点的距离值全部更新一次(即所有的点都算一次经过A点的距离。)这样我们可以更新从set1出发到其它点的距离。
以下是代码。在此代码中,我们使用邻接矩阵来表示图,如果是稀疏图,其实应该用邻接链表来表示,也就是前面所述的Bag,这里就不详述啦:
package summarize;
import java.util.ArrayList;
public class GraphDemo {
private static int M = Integer.MAX_VALUE; // 表示此路不可通
// http://www.geeksforgeeks.org/greedy-algorithms-set-6-dijkstras-shortest-path-algorithm/
// 以上为不能再优美的C语言实现。
public static void main(String[] args) {
int[][] w = { // 用邻接表矩阵表示的无向图
{0, 3, 2000, 7, M},
{3, 0, 4, 2, M},
{M, 4, 0, 5, 4},
{7, 2, 5, 0, 6},
{M, M , 4, 6, 0}
};
int[][] w2 = { // 用邻接表矩阵表示的无向图
{0, 10, M, 30, 100},
{M, 0, 50, M, M},
{M, M, 0, M, 10},
{M, M, 20, 0, 60},
{M, M, M, M, 0}
};
int start = 0;
int[] shortPath = dijkstra(w2, start);
for (int i = 0; i < shortPath.length; i++) {
System.out.println("The shortest path length from start to " + i + " is:" + shortPath[i]);
}
}
public static int[] dijkstra(int[][] graph, int src) {
if (graph == null || graph.length == 0 || graph[0].length == 0) {
return null;
}
// get to know the number of the vertexs.
int v = graph.length;
// We need a indicator to know if we have visited the vertex.
boolean visit[] = new boolean[v];
// record the length result.
int[] pathLen = new int[v];
// record the path.
ArrayList> path = new ArrayList>();
for (int i = 0; i < v; i++) {
path.add(new ArrayList());
path.get(i).add(0);
path.get(i).add(i);
}
// setup the source vertex;
visit[0] = true;
pathLen[0] = 0;
// stop when all the vertices has been added into the result set.
for (int i = 0; i < v - 1; i++) {
int minLen = M;
int minIndex = -1;
for (int j = 0; j < v; j++) {
// sometimes there is no route, so just let equal also return.
// so we use graph[src][j] <= minLen
if (!visit[j] && graph[src][j] <= minLen) {
minLen = graph[src][j];
minIndex = j;
}
}
// get the new shortest path, add it into the solution set.
visit[minIndex] = true;
pathLen[minIndex] = graph[src][minIndex];
// update all the neighbors of the new vertex.
for (int k = 0; k < v; k++) {
// if the path which pass the minIndex is shorter than the former path,
// just update it.
if (!visit[k]
&& graph[src][minIndex] != M
&& graph[minIndex][k] != M
&& graph[src][minIndex] + graph[minIndex][k] < graph[src][k]) {
graph[src][k] = graph[src][minIndex] + graph[minIndex][k];
path.set(k, new ArrayList(path.get(minIndex)));
path.get(k).add(k);
}
}
}
for (ArrayList array: path) {
System.out.println(array.toString());
}
return pathLen;
}
}
测试结果:
[0, 0]
[0, 1]
[0, 3, 2]
[0, 3]
[0, 3, 2, 4]
The shortest path length from start to 0 is:0
The shortest path length from start to 1 is:10
The shortest path length from start to 2 is:50
The shortest path length from start to 3 is:30
The shortest path length from start to 4 is:60