求有向图中两节点最短路径时应该很经典的问题,常用的算法有dikstra算法和floyd算法,今天我们就来研究这两种算法,我
把两种算法封装为一个工具类,便于调用,如下。
package com.ityang.solve;
import java.util.ArrayList;
import java.util.List;
public class ShortestPathUtils {
/**
* 封装路径信息的类
* @author Administrator
*
*/
public static class Path {
public int lengh; //路径长度
public List<Integer> pathBackup; //路径所经过的所有点
}
/**
*
* @param source 起始点下标
* @param dest 终点下标
* @param num 图中节点个数
* @param costs 图的邻接矩阵(两节点间无路径相连,用Integer.MAX_VALUE/2表示
* @return 路径 无路径时返回空
*/
public static Path dikstraShortestSinglePath(int source, int dest, int num,
int[][] costs) {
int[] d = new int[num];
//标示source到其它节点是否已经求出最短路径
boolean[] isShort = new boolean[num];
//路径还原时用,存放路径中上一个节点下标
int[] pre = new int[num];
//初始化
for (int i = 0; i < num; i++) {
isShort[i] = false;
}
for (int i = 0; i < num; i++) {
pre[i] = -1;
}
//dikstra算法求最短路径
dikstra(source, num, isShort, pre, costs, d);
if (d[dest] == Integer.MAX_VALUE/2) {
return null;
}
Path path = new Path();
path.lengh = d[dest];
//还原路径
List<Integer> pathBackup = new ArrayList<>();
pathBackup.add(dest);
for (int i = dest; pre[i] != -1;) {
pathBackup.add(i = pre[i]);
}
pathBackup.add(source);
path.pathBackup = pathBackup;
return path;
}
/**
*
* @param s
* @param num
* @param isShort
* @param pre
* @param costs
* @param d
*/
private static void dikstra(int s, int num, boolean[] isShort, int[] pre,
int[][] costs, int[] d) {
fill(s, num, costs, d);
while (true) {
int i = getMinIndex(isShort, num, d);
if (i == -1) {
break;
}
//i号节点已求出到source节点的最短路径
isShort[i] = true;
for (int j = 0; j < num; j++) {
if (!isShort[j] && (d[j] > d[i] + costs[i][j])) {
d[j] = d[i] + costs[i][j];
//存放上一节点下标
pre[j] = i;
}
}
}
}
/**
* 在未求出最短路径的所有节点中选出路径最小值
* @param isShort
* @param num
* @param d
* @return
*/
private static int getMinIndex(boolean[] isShort, int num, int[] d) {
int i = -1;
for (int u = 0; u < num; u++) {
if (!isShort[u] && (i == -1 || d[u] < d[i]))
i = u;
}
return i;
}
/**
* 路径长度初始化
* @param s
* @param num
* @param costs
* @param d
*/
private static void fill(int s, int num, int[][] costs, int[] d) {
for (int i = 0; i < num; i++) {
d[i] = costs[s][i];
}
}
/**
*
* @param source
* @param dest
* @param num
* @param costs
* @return
*/
public static Path floydShortestSinglePath(int source, int dest, int num,
int[][] costs) {
int[][] mid = new int[num][num];
for (int i = 0; i < num; i++) {
for (int j = 0; j < num; j++) {
mid[i][j] = -1;
}
}
for (int k = 0; k < num; k++) {
for (int i = 0; i < num; i++) {
for (int j = 0; j < num; j++) {
if (costs[i][j] > costs[i][k] + costs[k][j]){
costs[i][j] = costs[i][k] + costs[k][j];
mid[i][j]=k;
}
}
}
}
if (costs[source][dest] == Integer.MAX_VALUE/2 )
return null;
Path path = new Path();
path.lengh = costs[source][dest];
List<Integer> pathBackup = new ArrayList<>();
pathBackup.add(source);
while(mid[source][dest]!=-1){
pathBackup.add(mid[source][dest]);
source = mid[source][dest];
}
pathBackup.add(dest);
path.pathBackup = pathBackup;
return path;
}
}
现在看调用工具类计算最短路径
package com.ityang.solve;
import com.ityang.solve.ShortestPathUtils.Path;
public class GraphShortest {
private static int[][] costs = {
{ 0, 2, Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 3, 1 },
{ Integer.MAX_VALUE / 2, 0, 2, Integer.MAX_VALUE / 2,
Integer.MAX_VALUE / 2, 2 },
{ Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 0, 1,
Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2 },
{ Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2,
Integer.MAX_VALUE / 2, 0, 4, 6 },
{ Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2,
Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 0,
Integer.MAX_VALUE / 2 },
{ 3, Integer.MAX_VALUE / 2, 4, 3, 1, 0 } };
public static void main(String[] args) {
Path path1 = ShortestPathUtils.dikstraShortestSinglePath(0, 3, 6, costs);
Path path2 = ShortestPathUtils.floydShortestSinglePath(0, 3, 6, costs);
if(path1!=null){
System.out.println("路径长度:"+path1.lengh);
System.out.println("路径如下");
for (int i=path1.pathBackup.size()-1;i>=0;i--) {
System.out.print(path1.pathBackup.get(i)+" ");
}
}
if(path2!=null){
System.out.println("路径长度:"+path2.lengh);
System.out.println("路径如下");
for (int i=0;i<path2.pathBackup.size();i++) {
System.out.print(path2.pathBackup.get(i)+" ");
}
}
}
}
控制台输出结果
dikstra算法计算路径长度:4
路径如下
0 5 3
floyd算法计算路径长度:4
路径如下
0 5 3
后面我有输出了运行时间,来看看两种算法的效率吧
dikstra开始:1458285239203
dikstra结束:1458285239203
floyd开始1458285239203
floyd结束1458285239203
由于我这里只有6个节点,所以看不出什么差距,用时都接近为0,大家可以试试更多的节点,更复杂的路径,哪有就能看出两种算法的差别了。