数据结构编程笔记二十一:第七章 图 最短路径算法的实现
来源:互联网 发布:核盾网络验证使用方法 编辑:程序博客网 时间:2024/05/22 16:01
上次我们介绍了图的最小生成树算法的实现,这次介绍基于邻接矩阵的最短路径算法的实现。
还是老规矩:
程序在码云上可以下载。
地址:https://git.oschina.net/601345138/DataStructureCLanguage.git
本次最短路径共用到以下源文件,有一些已经在之前的文章介绍过。还是和以前一样,所有源文件需要放在同一目录下编译。
my_constants.h 各种状态码定义
MGraph.h 图的邻接矩阵存储结构表示定义
MGraph.cpp 基于邻接矩阵的基本操作实现
ShortestPath.cpp 最短路径算法实现
最短路径测试.cpp 主函数,调用算法完成最短路径程序的演示
邻接矩阵在《数据结构编程笔记十八:第七章 图 图的邻接矩阵存储表示及各基本操作的实现》一文有所介绍,my_constants.h、MGraph.h 和MGraph.cpp三个源文件与此文中的同名源文件内容完全一致,没有修改。这里不再重复贴了(否则文章会很长,不能突出重点),但在码云上你可以下载到全部源文件,我会把它放在一个目录下。
本次只贴最短路径的核心代码和主函数:
源文件:ShortestPath.cpp
//--------------- 最短路径 ------------------- //---------------------------迪杰斯特拉算法辅助数组的定义--------------------------------//P[][]记录了v->v0的最短路径经过哪些路径,//当P[i][j] = TRUE,说明i->j这样一条边在最短路径上//当P[i][j] = FALSE,说明i->j不在最短路径上 typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//D[]记录了v0到各个顶点的最短距离 typedef int ShortPathTable[MAX_VERTEX_NUM];/* (迪斯特拉算法求单源最短路径) 函数:ShortestPath_DIJ 参数:MGraph G 图G int v0 出发点v0 PathMatrix &P 记录最短路径经过哪些路径的P数组 ShortPathTable &D 记录v0到各个顶点的最短距离的D数组 返回值: 无 作用:用Dijkstra算法求有向图G的顶点v0顶点到其余顶点v的最短路径P[v]及其带权长度D[v] 若P[v][w]为TRUE,则w是从v0到v当前求得最小路径上的顶点 final[v]为TRUE当且仅当v∈S,即已经求得v0到v的最短路径*/void ShortestPath_DIJ(MGraph G, int v0, PathMatrix &P, ShortPathTable &D){ // //临时变量 int v, w, j, min; //final数组的作用是记录各个顶点是否已经找到最短路径 Status final[MAX_VERTEX_NUM]; //初始化数据 for(v = 0; v < G.vexnum; ++v) { //final数组中的值为FALSE表示还未求得最短路径 final[v] = FALSE; //将与v相邻的顶点加上权值 D[v] = G.arcs[v0][v].adj; //设P数组的初始值为没有路径 for(w = 0; w < G.vexnum; ++w) { P[v][w] = FALSE; }//for //D数组有权值即P数组对应位置存在路径 //也就是v和v0之间存在路径 if(D[v] < INFINITY){ //经过v0 P[v][v0] = TRUE; //经过v P[v][v] = TRUE; }//if }//for //v0->v0的距离是0 D[v0] = 0; //v0顶点并入S集 final[v0] = TRUE; //开始主循环,每次求得v0到某个顶点v的最短路径,并加v到S集 for(int i = 0; i < G.vexnum; ++i){ //其余G.vexnum-1个顶点 //当前所知离v0顶点最近距离,设置初始值∞ min = INFINITY; //扫描所有顶点 for(int w = 0; w < G.vexnum; ++w) { //w顶点没有并入集合S中 if(!final[w]) { //w顶点离v0顶点更近 if(D[w] < min){ //v记录了离v0最近的顶点 v = w; //min记录了到v0的最短路径 min = D[w]; }//if }//if }//for //离v0顶点最近的v加入S集 final[v] = TRUE; //更新当前最短路径及距离 //根据新并入的顶点,更新不在S集的顶点到V0的距离和路径数组 for(int w = 0; w < G.vexnum; ++w) { //修改D[w]和P[w],w∈V-S if(!final[w] && (min + G.arcs[v][w].adj < D[w])){ //更新D[w] D[w] = min + G.arcs[v][w].adj; //修改P[w],v0到w经过的顶点包括v0到v经过的顶点再加上顶点w //P[w] = P[v] + [w] for(int j = 0; j < G.vexnum; ++j) { P[w][j] = P[v][j]; }//for //最短路径经过w点 P[w][w] = TRUE; }//if }//for }//for }//ShortestPath_DIJ//---------------------弗洛伊德算法辅助数组的定义------------------------//P[][][]记录了最短路径经过了哪些路径 typedef int PathMatrix1[MAX_VERTEX_NUM][MAX_VERTEX_NUM][MAX_VERTEX_NUM];//D[][]记录了各顶点之间最短路径的值 typedef int DistanceMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];/* (弗洛伊德算法求各顶点之间的最短路径) 函数:ShortestPath_FLOYD 参数:MGraph G 图G PathMatrix1 &P 记录最短路径经过哪些路径的P数组 DistanceMatrix &D 记录了各顶点之间最短路径值的D数组 返回值: 无 作用:用Floyd算法求有向网G中各对顶点v和w之间的最短路径P[v][w]及其带权长度D[v][w]。 若P[v][w][u]为TRUE,则u是从v到w当前求得最短路径上的顶点。*/void ShortestPath_FLOYD(MGraph G, PathMatrix1 &P, DistanceMatrix &D){ //各对结点之间的初始已知路径及距离 for(int v = 0; v < G.vexnum; ++v) { for(int w = 0; w < G.vexnum; ++w){ //顶点v到顶点w的直接距离 D[v][w] = G.arcs[v][w].adj; //路径矩阵的初值为FALSE for(int u = 0; u < G.vexnum; ++u) { P[v][w][u] = FALSE; }//for //从v到w有直接路径 if(D[v][w]<INFINITY){ //从v到w的路径经过v和w两点 P[v][w][v] = TRUE; P[v][w][w] = TRUE; }//if }//for }//for //从v经u到w的一条路径更短 for(int u = 0; u < G.vexnum; ++u) { for(int v = 0; v < G.vexnum; ++v) { for(int w = 0; w < G.vexnum; ++w) { //从v经u到w的一条路径更短 if(D[v][u] + D[u][w] < D[v][w]){ //更新最短距离 D[v][w] = D[v][u] + D[u][w]; //从v到w的路径经过从v到u和从u到w的所有路径 for(int i = 0; i < G.vexnum; ++i) { P[v][w][i] = P[v][u][i] || P[u][w][i]; }//for }//if }//for }//for }//for}//ShortestPath_FLOYD
源文件:最短路径测试.cpp
//**************************引入头文件*****************************#include <stdio.h> //使用了标准库函数 #include <stdlib.h> //使用了动态内存分配函数#include "my_constants.h" //引入自定义的符号常量,主要是状态码 #include "MGraph.h" //引入图的邻接矩阵表示法的基本定义 #include "MGraph.cpp" //引入图的主要操作#include "ShortestPath.cpp" //引入图的最短路径算法实现 //----------------------主函数----------------------int main(int argc, char *argv[]){ printf("\n-------------基于邻接矩阵的最短路径算法测试程序--------------\n\n"); //图G MGraph G; //临时变量,保存输入的顶点 VertexType v1, v2; //临时变量,保存输入的顶点数 int n; //图的创建 printf("->测试图的创建:(最短路径算法要求图必须是有向网,请选择1)\n"); CreateGraph(G); //打印邻接矩阵 printf("\n->创建成功后图的邻接矩阵:\n\n"); PrintAdjMatrix(G); //测试最短路径 if(G.kind == DN){ //迪杰斯特拉算法用到的临时变量: int v0 = 0; //源点 PathMatrix P; //P数组 ShortPathTable D; //D数组 printf("\n->测试求有向网的最短路径:\n\n"); //测试迪杰斯特拉算法 printf("->使用迪杰斯特拉算法求单源最短路径:\n"); ShortestPath_DIJ(G, v0, P, D); //打印迪杰斯特拉算法用的P数组 printf("->迪杰斯特拉算法用到的最短路径数组P[i][j]如下(T表示TRUE,F表示FALSE):\n"); for(int i = 0; i < G.vexnum; ++i) { for(int j = 0; j < G.vexnum; ++j) { printf(" %3c ", P[i][j] == 1 ? 'T' : 'F'); }//for printf("\n"); }//for //打印迪杰斯特拉算法用的D数组 printf("顶点%d到其他顶点的最短路径如下:\n", v0); for(int i = 0; i < G.vexnum; ++i) { printf("%d ---> %d :%3d\n", v0, G.vexs[i], D[i]); }//for //弗洛伊德算法用到的两个数组 //这两个数组和迪斯特拉算法的两个数组虽然同名但没有关系 PathMatrix1 P1; //P数组 DistanceMatrix D1; //D数组 //测试弗洛伊德算法 printf("->使用弗洛伊德算法求任意两顶点最短路径:\n"); //ShortestPath_FLOYD()要求对角线元素为0 for(int i = 0; i < G.vexnum; ++i) { G.arcs[i][i].adj = 0; }//for printf("->执行算法前的邻接矩阵如下:\n"); PrintAdjMatrix(G); //执行弗洛伊德算法 ShortestPath_FLOYD(G, P1, D1); //打印弗洛伊德算法用的D数组 printf("->弗洛伊德算法用的D数组:\n\n"); //输出左上角多余的空白 printf(" "); //输出邻接矩阵的上坐标(全部顶点) for(int i = 0; i < G.vexnum; i++) { printf(" %3d ", i); }//for printf("\n"); //输出左上角多余的空白 printf(" +"); //输出一条横线 for(int i = 0; i < G.vexnum; i++) { printf("-----"); }//for printf("\n"); for(int i = 0; i < G.vexnum; ++i) { //输出邻接矩阵左边的坐标 printf(" %3d |", G.vexs[i]); for(int j = 0; j < G.vexnum; ++j) { if(D1[i][j] == INFINITY) { printf(" ∞ "); }//if else { printf(" %3d ", D1[i][j]); }//else }//for printf("\n |\n"); }//for //打印计算得到的最短路径 printf("\n->最短路径的计算结果:\n"); for(int i = 0; i < G.vexnum; ++i) { for(int j = 0; j < G.vexnum; ++j) { printf(" %d--->%d :%-6d ", G.vexs[i], G.vexs[j], D1[i][j]); }//for printf("\n"); }//for //打印弗洛伊德算法用的P数组 printf("\n->弗洛伊德算法用的最短路径数组P[i][j][k]如下(T表示TRUE,F表示FALSE):\n"); for(int i = 0; i < G.vexnum; ++i) { for(int j = 0; j < G.vexnum; ++j) { for(int k = 0; k < G.vexnum; ++k){ printf("P[%d][%d][%d] = %c ", i, j, k, P1[i][j][k] == 1 ? 'T' : 'F'); }//for printf("\n"); }//for }//for }//if //测试销毁 printf("\n->测试销毁图: "); DestroyGraph(G); printf("成功!\n"); printf("演示结束,程序退出!\n"); return 0;}
测试的有向网使用书上P188页的图7.34,可以直接去该页寻找最短路径的正确结果,以便于验证算法正确与否。
测试过程中程序的输入和输出:
-------------基于邻接矩阵的最短路径算法测试程序--------------->测试图的创建:(最短路径算法要求图必须是有向网,请选择1)请输入您想构造的图的类型:有向图输入0,有向网输入1,无向图输入2,无向网输入3):1请依次输入有向网G的顶点数,弧数,用逗号隔开6,8请依次输入有向网G的顶点名称,用空格隔开0 1 2 3 4 5请依次输入有向网G每条弧依附的两顶点名称及权值,输完一组按回车0 5 1000 4 300 2 101 2 52 3 504 3 204 5 603 5 10->创建成功后图的邻接矩阵: 0 1 2 3 4 5 +------------------------------ 0 | ∞ ∞ 10 ∞ 30 100 | 1 | ∞ ∞ 5 ∞ ∞ ∞ | 2 | ∞ ∞ ∞ 50 ∞ ∞ | 3 | ∞ ∞ ∞ ∞ ∞ 10 | 4 | ∞ ∞ ∞ 20 ∞ 60 | 5 | ∞ ∞ ∞ ∞ ∞ ∞ |->测试求有向网的最短路径:->使用迪杰斯特拉算法求单源最短路径:->迪杰斯特拉算法用到的最短路径数组P[i][j]如下(T表示TRUE,F表示FALSE): F F F F F F F F F F F F T F T F F F T F F T T F T F F F T F T F F T T T顶点0到其他顶点的最短路径如下:0 ---> 0 : 00 ---> 1 :655350 ---> 2 : 100 ---> 3 : 500 ---> 4 : 300 ---> 5 : 60->使用弗洛伊德算法求任意两顶点最短路径:->执行算法前的邻接矩阵如下: 0 1 2 3 4 5 +------------------------------ 0 | 0 ∞ 10 ∞ 30 100 | 1 | ∞ 0 5 ∞ ∞ ∞ | 2 | ∞ ∞ 0 50 ∞ ∞ | 3 | ∞ ∞ ∞ 0 ∞ 10 | 4 | ∞ ∞ ∞ 20 0 60 | 5 | ∞ ∞ ∞ ∞ ∞ 0 |->弗洛伊德算法用的D数组: 0 1 2 3 4 5 +------------------------------ 0 | 0 ∞ 10 50 30 60 | 1 | ∞ 0 5 55 ∞ 65 | 2 | ∞ ∞ 0 50 ∞ 60 | 3 | ∞ ∞ ∞ 0 ∞ 10 | 4 | ∞ ∞ ∞ 20 0 30 | 5 | ∞ ∞ ∞ ∞ ∞ 0 |->最短路径的计算结果: 0--->0 :0 0--->1 :65535 0--->2 :10 0--->3 :50 0--->4 :30 0--->5 :60 1--->0 :65535 1--->1 :0 1--->2 :5 1--->3 :55 1--->4 :65535 1--->5 :65 2--->0 :65535 2--->1 :65535 2--->2 :0 2--->3 :50 2--->4 :65535 2--->5 :60 3--->0 :65535 3--->1 :65535 3--->2 :65535 3--->3 :0 3--->4 :65535 3--->5 :10 4--->0 :65535 4--->1 :65535 4--->2 :65535 4--->3 :20 4--->4 :0 4--->5 :30 5--->0 :65535 5--->1 :65535 5--->2 :65535 5--->3 :65535 5--->4 :65535 5--->5 :0->弗洛伊德算法用的最短路径数组P[i][j][k]如下(T表示TRUE,F表示FALSE):P[0][0][0] = T P[0][0][1] = F P[0][0][2] = F P[0][0][3] = F P[0][0][4] = F P[0][0][5] = FP[0][1][0] = F P[0][1][1] = F P[0][1][2] = F P[0][1][3] = F P[0][1][4] = F P[0][1][5] = FP[0][2][0] = T P[0][2][1] = F P[0][2][2] = T P[0][2][3] = F P[0][2][4] = F P[0][2][5] = FP[0][3][0] = T P[0][3][1] = F P[0][3][2] = F P[0][3][3] = T P[0][3][4] = T P[0][3][5] = FP[0][4][0] = T P[0][4][1] = F P[0][4][2] = F P[0][4][3] = F P[0][4][4] = T P[0][4][5] = FP[0][5][0] = T P[0][5][1] = F P[0][5][2] = F P[0][5][3] = T P[0][5][4] = T P[0][5][5] = TP[1][0][0] = F P[1][0][1] = F P[1][0][2] = F P[1][0][3] = F P[1][0][4] = F P[1][0][5] = FP[1][1][0] = F P[1][1][1] = T P[1][1][2] = F P[1][1][3] = F P[1][1][4] = F P[1][1][5] = FP[1][2][0] = F P[1][2][1] = T P[1][2][2] = T P[1][2][3] = F P[1][2][4] = F P[1][2][5] = FP[1][3][0] = F P[1][3][1] = T P[1][3][2] = T P[1][3][3] = T P[1][3][4] = F P[1][3][5] = FP[1][4][0] = F P[1][4][1] = F P[1][4][2] = F P[1][4][3] = F P[1][4][4] = F P[1][4][5] = FP[1][5][0] = F P[1][5][1] = T P[1][5][2] = T P[1][5][3] = T P[1][5][4] = F P[1][5][5] = TP[2][0][0] = F P[2][0][1] = F P[2][0][2] = F P[2][0][3] = F P[2][0][4] = F P[2][0][5] = FP[2][1][0] = F P[2][1][1] = F P[2][1][2] = F P[2][1][3] = F P[2][1][4] = F P[2][1][5] = FP[2][2][0] = F P[2][2][1] = F P[2][2][2] = T P[2][2][3] = F P[2][2][4] = F P[2][2][5] = FP[2][3][0] = F P[2][3][1] = F P[2][3][2] = T P[2][3][3] = T P[2][3][4] = F P[2][3][5] = FP[2][4][0] = F P[2][4][1] = F P[2][4][2] = F P[2][4][3] = F P[2][4][4] = F P[2][4][5] = FP[2][5][0] = F P[2][5][1] = F P[2][5][2] = T P[2][5][3] = T P[2][5][4] = F P[2][5][5] = TP[3][0][0] = F P[3][0][1] = F P[3][0][2] = F P[3][0][3] = F P[3][0][4] = F P[3][0][5] = FP[3][1][0] = F P[3][1][1] = F P[3][1][2] = F P[3][1][3] = F P[3][1][4] = F P[3][1][5] = FP[3][2][0] = F P[3][2][1] = F P[3][2][2] = F P[3][2][3] = F P[3][2][4] = F P[3][2][5] = FP[3][3][0] = F P[3][3][1] = F P[3][3][2] = F P[3][3][3] = T P[3][3][4] = F P[3][3][5] = FP[3][4][0] = F P[3][4][1] = F P[3][4][2] = F P[3][4][3] = F P[3][4][4] = F P[3][4][5] = FP[3][5][0] = F P[3][5][1] = F P[3][5][2] = F P[3][5][3] = T P[3][5][4] = F P[3][5][5] = TP[4][0][0] = F P[4][0][1] = F P[4][0][2] = F P[4][0][3] = F P[4][0][4] = F P[4][0][5] = FP[4][1][0] = F P[4][1][1] = F P[4][1][2] = F P[4][1][3] = F P[4][1][4] = F P[4][1][5] = FP[4][2][0] = F P[4][2][1] = F P[4][2][2] = F P[4][2][3] = F P[4][2][4] = F P[4][2][5] = FP[4][3][0] = F P[4][3][1] = F P[4][3][2] = F P[4][3][3] = T P[4][3][4] = T P[4][3][5] = FP[4][4][0] = F P[4][4][1] = F P[4][4][2] = F P[4][4][3] = F P[4][4][4] = T P[4][4][5] = FP[4][5][0] = F P[4][5][1] = F P[4][5][2] = F P[4][5][3] = T P[4][5][4] = T P[4][5][5] = TP[5][0][0] = F P[5][0][1] = F P[5][0][2] = F P[5][0][3] = F P[5][0][4] = F P[5][0][5] = FP[5][1][0] = F P[5][1][1] = F P[5][1][2] = F P[5][1][3] = F P[5][1][4] = F P[5][1][5] = FP[5][2][0] = F P[5][2][1] = F P[5][2][2] = F P[5][2][3] = F P[5][2][4] = F P[5][2][5] = FP[5][3][0] = F P[5][3][1] = F P[5][3][2] = F P[5][3][3] = F P[5][3][4] = F P[5][3][5] = FP[5][4][0] = F P[5][4][1] = F P[5][4][2] = F P[5][4][3] = F P[5][4][4] = F P[5][4][5] = FP[5][5][0] = F P[5][5][1] = F P[5][5][2] = F P[5][5][3] = F P[5][5][4] = F P[5][5][5] = T->测试销毁图: 成功!演示结束,程序退出!--------------------------------Process exited with return value 0Press any key to continue . . .
总结:
迪杰斯特拉算法求得的是单源最短路径,想得到所有顶点之间的最短路径只能一遍遍的循环去求。
弗洛伊德算法可以求得各顶点之间的最短路径,且形式更加优雅。
下次文章会介绍拓扑排序算法的实现。感谢大家一直以来的支持和关注。再见!
- 数据结构编程笔记二十一:第七章 图 最短路径算法的实现
- 数据结构编程笔记二十三:第七章 图 关键路径算法的实现
- 数据结构编程笔记二十:第七章 图 最小生成树算法的实现
- 数据结构编程笔记二十二:第七章 图 拓扑排序算法的实现
- 数据结构_7:图算法 :最短路径
- 算法系列笔记8(有关图的算法二—最短路径问题)
- 【数据结构与算法】 PROJECT 3 有向图的最短路径实现 之 知识点
- 【数据结构与算法】 有向图的最短路径实现
- 大话数据结构 code 第七章 07最短路径_Dijkstra
- 大话数据结构 code 第七章 08最短路径_Floyd
- 【学习笔记----数据结构20-图的最短路径】
- 图的最短路径算法(二)-任意节点最短路径
- 【数据结构】最短路径算法
- floyd最短路径算法的实现
- A*算法的最短路径实现!
- Dijkstra算法的最短路径实现
- dijkstra最短路径算法的实现
- 【数据结构笔记】10:三个最短路径算法注解
- JBOSS配置文档
- 张小龙:微信背后的产品观
- cnpm 将 node-sass 也镜像了
- httpclient 学习
- angularjs自定义指令属性详解
- 数据结构编程笔记二十一:第七章 图 最短路径算法的实现
- 写在大二开学之际~
- [Caffe]: 关于dropout的实现细节
- 主成分分析理论与MATLAB实现示例
- 【数据库】连接池的作用
- Openlayers2切换supermap WMS服务底图,已解决
- Spring容器创建对象的三种方式
- 在pyspark中调用scala代码
- Java创建线程的两个方法