最短路径算法
原文链接:https://blog.csdn.net/xiaoxi_hahaha/article/details/110257368
https://blog.csdn.net/qq_25954259/article/details/78289335/
原文链接:https://blog.csdn.net/jeffleo/article/details/53349825
已知起始结点,求最短路径的问题。适合使用Dijkstra算法。迪杰斯特拉算法是由荷兰计算机科学家在1956年发现的算法,此算法使用类似广度优先搜索的方法解决了带权图的单源最短路径问题。它是一个贪心算法。
全局最短路径问题- 求图中所有的最短路径。适合使用Floyd-Warshall算法
最短路径是一个图中2个点的最短距离。
最小生成树是连接所有的点的路径最短,但是不一定是任意两点的距离最小。
Dijkstra算法:
首先,可以设置两个集合分别是A和B,A用来存放已经求出最短路径的点,B用来存放还未计算出最短路径的点,
我们从图中任选一点来解题,假设我们将源点source选择在” 0 "这个点。一开始所有点到达源点0的距离我们假设为∞,代表不可达。源点0到自己本身的距离为0,初始化如下:此时A集合为:{0},B集合为:{1,2,3,4,5,6}
第一步:从0点开始,更新和0邻接的所有点的距离,此时,因为与0邻接的有1和2,并且到这两个点的距离,小于原来的∞距离,所以要将这两个点到0的距离都进行更新如下图,
第二步:从B集合里面选择一个点加入A集合,这个点要满足距离0点的距离最短,因此我们选择2这个点添加到集合A,此时集合A变为:{0,2},集合B变为:{1,3,4,5,6},如下图
第三步:选择刚刚加入的这个2点,更新所有与2点邻接的点,因为与2邻接的点有3和5,并且这两个点到0点的距离小于原来它们到0点的距离∞
第四步:从B集合里面选择1这点加入到集合A中,因为1这个点在B集合中距离0最近,如下图,此时A集合变成:{0,1,2},B集合变成:{3,4,5,6}
第五步:选择刚刚加入的1这个点,更新1所有的邻接点,它的邻接点有3和4,因为此刻从0到3的距离为6,小于原来0到3的距离8,因此这个时候到6的距离更新为6(5+1),此时0到4的距离被更新为11
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace shortestpath { class Program { static int[,] graph = new int[6, 6] { { 10000, 10000, 10, 10000, 30, 100 }, { 10000, 10000, 5, 10000, 10000, 10000 }, { 10000, 10000, 10000, 50, 10000, 10000 }, { 10000, 10000, 10000, 10000, 10000, 10 }, { 10000, 10000, 10000, 20, 10000, 60 }, { 10000, 10000, 10000, 10000, 10000, 10000 } }; static int[] S = new int[6] { 0, 0, 0, 0, 0, 0 };//最短路径的顶点集合 static string[] mid = new string[6]{"","","","","",""};//点的路线 public static int IsContain(int m)//判断元素是否在mst中 { int index = -1; for (int i = 1; i < 6; i++) { if (S[i] == m) { index = i; } } return index; } /// <summary> /// Dijkstrah实现最短路算法 /// </summary> static void ShortestPathByDijkstra() { int min; int next; for (int f = 5; f > 0;f--) { //置为初始值 min = 1000; next = 0;//第一行最小的元素所在的列 next点 //找出第一行最小的列值 for (int j = 1; j < 6; j++)//循环第0行的列 { if ((IsContain(j) == -1) && (graph[0, j] < min))//不在S中,找出第一行最小的元素所在的列 { min = graph[0, j]; next = j; } } //将下一个点加入S S[next] = next; //输出最短距离和路径 if (min == 1000) { Console.WriteLine("V0到V{0}的最短路径为:无", next); } else { Console.WriteLine("V0到V{0}的最短路径为:{1},路径为:V0{2}->V{0}", next, min, mid[next]); } // 重新初始0行所有列值 for (int j = 1; j < 6; j++)//循环第0行的列 { if (IsContain(j) == -1)//初始化除包含在S中的 { if ((graph[next, j] + min) < graph[0, j])//如果小于原来的值就替换 { graph[0, j] = graph[next, j] + min; mid[j] = mid[next] + "->V" + next;//记录过程点 } } } } } static void Main(string[] args) { ShortestPathByDijkstra(); } } }
弗洛伊德算法:
弗洛伊德基本思想
弗洛伊德算法作为求最短路径的经典算法,其算法实现相比迪杰斯特拉等算法是非常优雅的,可读性和理解都非常好。
基本思想:
弗洛伊德算法定义了两个二维矩阵:
矩阵D记录顶点间的最小路径
例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10;
矩阵P记录顶点间最小路径中的中转点
例如P[0][3]= 1 说明,0 到 3的最短路径轨迹为:0 -> 1 -> 3。
它通过3重循环,k为中转点,v为起点,w为终点,循环比较D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 为更小值,则把D[v][k] + D[k][w] 覆盖保存在D[v][w]中。
概念是比较难理解的,我们来看图:
顶点名称和下标的对应
A B C D E F G
0 1 2 3 4 5 6
第2步:
以A为中间点,原D矩阵中,D[B][G]的值为INF,即不存在B->G的最小路径,但是通过A为中间点,D[B][A] + D[A][G] = 12 + 14 = 26 小于 D[B][G] = INF, 所以D[B][A] + D[A][G] 为 B -> G的最小值,因此覆盖D[B][G] 为 26。
第3步:
以B为中间点,第2步后的D矩阵中,D[A][C]的值为INF, 但是通过B,D[A][B] + D[B][C] = 12 + 10 = 22 小于 D[A][C] = INF,所以D[A][B] + D[B][C] 为 A->C的最小路径,覆盖D[A][C]的值为22, 以此类推。
第4步….
代码实现
我们就对上面的图进行弗洛伊德算法求最短路径,并且我们求A到D的最小路径,即v = 0, w = 3;
#include <stdio.h> #include <stdlib.h> #define MAXN 10 #define INF = 1000 typedef struct struct_graph{ char vexs[MAXN]; int vexnum;//顶点数 int edgnum;//边数 int matirx[MAXN][MAXN];//邻接矩阵 } Graph; int pathmatirx[MAXN][MAXN];//记录对应点的最小路径的前驱点,例如p(1,3) = 2 说明顶点1到顶点3的最小路径要经过2 int shortPath[MAXN][MAXN];//记录顶点间的最小路径值 void short_path_floyd(Graph G, int P[MAXN][MAXN], int D[MAXN][MAXN]){ int v, w, k; //初始化floyd算法的两个矩阵 for(v = 0; v < G.vexnum; v++){ for(w = 0; w < G.vexnum; w++){ D[v][w] = G.matirx[v][w]; P[v][w] = w; } } //这里是弗洛伊德算法的核心部分 //k为中间点 for(k = 0; k < G.vexnum; k++){ //v为起点 for(v = 0 ; v < G.vexnum; v++){ //w为终点 for(w =0; w < G.vexnum; w++){ if(D[v][w] > (D[v][k] + D[k][w])){ D[v][w] = D[v][k] + D[k][w];//更新最小路径 P[v][w] = P[v][k];//更新最小路径中间顶点 } } } } printf("\n初始化的D矩阵\n"); for(v = 0; v < G.vexnum; v++){ for(w = 0; w < G.vexnum; w++){ printf("%d ", D[v][w]); } printf("\n"); } printf("\n初始化的P矩阵\n"); for(v = 0; v < G.vexnum; v++){ for(w = 0; w < G.vexnum; w++){ printf("%d", P[v][w]); } printf("\n"); } v = 0; w = 3; //求 0 到 3的最小路径 printf("\n%d -> %d 的最小路径为:%d\n", v, w, D[v][w]); k = P[v][w]; printf("path: %d", v);//打印起点 while(k != w){ printf("-> %d", k);//打印中间点 k = P[k][w]; } printf("-> %d\n", w); } int main(){ int v, w; Graph G; printf("请输入顶点数:\n"); scanf("%d", &G.vexnum); printf("请输入初始矩阵值:\n"); for(v = 0; v < G.vexnum; v++){ for(w = 0; w < G.vexnum; w++){ scanf("%d", &G.matirx[v][w]); } } printf("\n输入的矩阵值:\n"); for(v = 0; v < G.vexnum; v++){ for(w = 0; w < G.vexnum; w++){ printf("%d ", G.matirx[v][w]); } printf("\n"); } short_path_floyd(G, pathmatirx, shortPath); }