第6章 最短路径
第1节 只有五行的算法–Floyd-Warshall
任意两点之间的最短路径。
for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(e[i][j]>e[i][k]+e[k][j]){ e[i][j] = e[i][k]+e[k][j]; } } } }
第2节 Dijkstra算法–通过边实现松弛
从指定点到其余点的最短路径。“单源最短路径”。
基本思想是每次找到源点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到所有点的最短路径。
package ch6; import java.util.Scanner; public class Test2 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt();//n个顶点 int m = sc.nextInt();//m条边 final int inf = 999999; int [][] e = new int[10][10];//存储地图 //初始化e for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) { e[i][j] = 0; }else { e[i][j] = inf; } } } //读入边 for(int i=1;i<=m;i++) { int t1 = sc.nextInt(); int t2 = sc.nextInt(); int t3 = sc.nextInt(); e[t1][t2] = t3; } //初始化dis数组, 1到各个顶点的路程 int[] dis = new int[10]; for(int i=1;i<=n;i++) { dis[i] = e[1][i]; } //初始化book int[] book = new int[10]; book[1] = 1; //Dijkstra算法核心 for(int i=1;i<=n-1;i++) { int min = inf; int u = 1; //遍历,找到当前最小距离顶点u for(int j=1;j<=n;j++) { if(book[j]==0 &&dis[j]<min) { min = dis[j]; u=j; } } book[u] = 1; //通过u对其余顶点进行扩展 “松弛” for(int v=1;v<=n;v++) { if(e[u][v]<inf) { if(dis[v]>dis[u]+e[u][v]) { dis[v] = dis[u]+e[u][v]; } } } } //输出结果 for(int i=1;i<=n;i++) { System.out.print(dis[i]+" "); } } }
对于边数m小于n^2的稀疏图,可以用邻接表代替邻接矩阵,降低时间复杂度。
Scanner sc = new Scanner(System.in); int n = 5; int m = 4; int[] u = new int[m+1];//顶点(起点) int[] v = new int[m+1];//顶点(目的) int[] w = new int[m+1];//路程 //first和next在遍历顶点i的边时是第一条和下一条,但是存储时是最后一条和上一条。 //first[i]是顶点i的第一条边,有歧义。存储时是最近一次的读入顶点i位起点的边 int [] first = new int[n+1]; //next[i]是边i的下一条边。有歧义。存储时是上一次读入的顶点i为起点的边 int [] next = new int[m+1]; //初始化first ,-1代表顶点没有边 for(int i=1;i<=n;i++){ first[i] = -1; } //读入m条边 for(int i=1;i<=m;i++){ u[i] = sc.nextInt(); v[i] = sc.nextInt(); w[i] = sc.nextInt(); next[i] = first[u[i]]; first[u[i]] = i; } //遍历每个顶点的边 for(int i=1;i<=n;i++){ int k = first[i]; while(k!=-1){ System.out.print(""+u[k]+" "+v[k]+" "+w[k]+" "); k = next[k]; } }
第3节 Bellman-Ford —解决负权边
对所有的边进行n-1轮“松弛”操作。经过1轮松弛,得到的是从1号顶点“只能经过一条边”到其余各点的最短路径长度,经过2轮松弛,达到的是从1号顶点“最多经过2条边”到其余各点的最短路径长度。(不包含负权回路,负权回路时不存在最短路径)n个顶点的图中,任意两点之间最短路径最多包含n-1条边。因此,最多经过n-1轮松弛就能得到最短路径。
for(int k = 1; k<= n-1;k++){ for(int i = 1;i <= m;i++){ if(dis[v[i]]>dis[u[i]]+w[i]){ dis[v[i]]=dis[u[i]]+w[i]; } } }
第4节 Bellman-Ford的队列优化
每次仅对最短路径发生变化的点的邻边进行松弛,用队列记录这些发生变化的点。
选取队首顶点u,对u的出边进行松弛,松弛过程中用到的顶点v,把v加入队尾,同一个顶点不用重复加入。对u松弛完成后,将u出队列。接着对其余队首顶点进行重复上述操作。直到队列空为止。
package ch6; import java.util.Scanner; public class Test3 { public static void main(String[] args) { final int inf = 999999; Scanner sc = new Scanner(System.in); int n = sc.nextInt();//顶点数 int m = sc.nextInt();//边数 int[] u = new int[m+1];//顶点(起点) int[] v = new int[m+1];//顶点(目的) int[] w = new int[m+1];//路程 //first和next在遍历顶点i的边时是第一条和下一条,但是存储时是最后一条和上一条。 //first[i]是顶点i的第一条边,有歧义。存储时是最近一次的读入顶点i位起点的边 int [] first = new int[n+1]; //next[i]是边i的下一条边。有歧义。存储时是上一次读入的顶点i位起点的边 int [] next = new int[m+1]; //dis[i]是1号顶点到i号顶点的距离 int[] dis = new int[n+1]; for(int i=1;i<=n;i++) { dis[i] = inf; } dis[1] = 0; //book[i]为1表示i号顶点在队列中 int[] book = new int[n+1]; int[] que = new int[n+2]; int head = 1; int tail = 1; //初始化first ,-1代表顶点没有边 for(int i=1;i<=n;i++){ first[i] = -1; } //读入m条边 for(int i=1;i<=m;i++){ u[i] = sc.nextInt(); v[i] = sc.nextInt(); w[i] = sc.nextInt(); next[i] = first[u[i]]; first[u[i]] = i; } //1号顶点入队 que[tail] = 1; tail++; book[1] = 1; while(head<tail) { int k = first[que[head]];//队首顶点的第一条边 //松弛队首顶点的所有边 while(k!=-1) { if(dis[v[k]] > dis[u[k]]+w[k]) { dis[v[k]] = dis[u[k]]+w[k]; if(book[v[k]]==0) { que[tail]=v[k]; tail++; book[v[k]] = 1; } } k = next[k]; } //出队 book[que[head]]=0;//如果顶点最短路程估计值再次变小后,需要对其出边再次进行松弛 head++; } //输出到各点的最短路径 for(int i=1;i<=n;i++){ System.out.print(" "+dis[i]); } } }
测试数据: 5 7 1 2 2 1 5 10 2 3 3 2 5 7 3 4 4 4 5 5 5 3 6 结果: 0 2 5 9 9
第5节 最短路径算法对比分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-25AwzS6W-1589449758797)(C:\Users\LF\AppData\Roaming\Typora\typora-user-images\1589257208920.png)]
//输出到各点的最短路径 for(int i=1;i<=n;i++){ System.out.print(" "+dis[i]); } }
}
测试数据:
5 7
1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6
结果:
0 2 5 9 9
## 第5节 最短路径算法对比分析 [外链图片转存中...(img-25AwzS6W-1589449758797)]