弗洛伊德算法-寻找最短路径(无权/有权图)
前言:弗洛伊德算法-寻找最短路径学习和实现笔记
什么是弗洛伊德算法-寻找最短路径
个人理解,正常的一个图,将其转化为一个邻接矩阵,在邻接矩阵中,每一个权值实际上就是一个顶点到另外一个顶点之间的距离。
那这里我举个例子,比如如下的情况
首先需要知道下什么是中转顶点(我自己是这样叫的,野路子),比如图中的一个顶点1到一个顶点3之间可以通过中转顶点2到达,这种节点就称之为中转节点
-
第一次的时候,当加入了一个中转顶点1,之后那么接下来的顶点1,2,3,4,5都可以通过中转顶点1来达到原本直连是连接不到的顶点,又或者可以通过更少的权值来达到原本权值比较大的顶点
-
这里加入顶点1之后,那么我们这里从顶点1开始,但是你会发现从顶点1开始没有必要
原因是加入了中转顶点1,对于顶点1本身没有任何影响
-
那么我们这里就从顶点2开始,当对于顶点2来说,顶点1中转节点,那么顶点2到顶点1的距离还是10,但是顶点2到顶点5之间就可以通过顶点1来实现到达,那么在邻接矩阵中对于顶点2到顶点5来说之间的权值原本是无穷,但是这里是有向图,所以还是无法到达
-
接着顶点2到顶点3(通过顶点5无法到达),所以权值还是原来的50,那么顶点2到顶点4无法到达,因为这个是有向图
-
就这样以此类推即可完全各个顶点之间的最短路径
关于对最短路径的寻找
当你把各个顶点之间的最短路径求出来之后,那么要怎么寻找一个顶点到一个顶点之间的最短路径呢?首先要明白的就是最短路径是针对于顶点的
就比如说下面这张图,我要先找到一条顶点1到顶点5之间的最短路径
-
那么首先我先从第1行的第5列,我发现这个点是顶点4(下标0开始)
-
那么我就继续从第1行的第4列找,我发现它指向的是-1,那么这里是-1就说明顶点1到顶点4之间是没有中间路径的,那么此时的路径就是 顶点1 -> 顶点4
-
但是顶点4和顶点5之间不一定没有路径,所以这里还需要从第4行的第5列中找,因为不一定顶点4能够直接到达顶点5,可能有更小的权值的顶点在其中,发现这里指向的是顶点3(下标0开始)
-
所以这里又要继续在第4行的第3列中找,发现是-1
-
这里还需要看下第3行的第5列,发现也是-1
-
那到这里的话就结束了
-
顶点1到顶点4之间的最短路径就是 顶点1 -> 顶点4 -> 顶点3 -> 顶点5
那么如何通过代码实现寻找最短路径呢?
通过递归的方式能够来寻找最短路径,跟二分递归类似,代码在下面已经实现了
一道简单的练习题
实现弗洛伊德-寻找最短路径
#include<stdio.h> #include<stdlib.h> #include<string.h> #define MAX 32767 #define OK 1 #define ERROR 0 typedef int Status; typedef int ElemType; typedef struct _Graph{ int vexNum; int arcNum; int** arcs; char* vexs; }Graph, *PGraph; Graph* initGraph(int vexNum) { Graph* pGraph = NULL; if (pGraph == NULL) { pGraph = (Graph*)malloc(sizeof(Graph)); memset(pGraph, 0, sizeof(Graph)); if (pGraph == NULL) return NULL; pGraph->vexNum = vexNum; pGraph->vexs = (char*)malloc(sizeof(pGraph->vexNum)); memset(pGraph->vexs, 0, sizeof(pGraph->vexNum)); pGraph->arcNum = 0; pGraph->arcs = (int**)malloc(sizeof(int*)* pGraph->vexNum); memset(pGraph->arcs, sizeof(int*)* pGraph->vexNum, 0); for (int i = 0; i < pGraph->vexNum; i++) { pGraph->arcs[i] = (int*)malloc(sizeof(int)* pGraph->vexNum); memset(pGraph->arcs[i], 0, sizeof(int)*pGraph->vexNum); } } return pGraph; } Status createGraph(Graph** pGraph, char* vexs, int* arcs) { if (*pGraph == NULL) return ERROR; for (int i = 0; i<(*pGraph)->vexNum; i++) { *((*pGraph)->vexs + i) = *(vexs + i); for (int j = 0; j<(*pGraph)->vexNum; j++) { (*pGraph)->arcs[i][j] = *(arcs + i*((*pGraph)->vexNum) + j); printf("%d ", (*pGraph)->arcs[i][j]); if ((*pGraph)->arcs[i][j] != 0 && (*pGraph)->arcs[i][j] != MAX) (*pGraph)->arcNum++; } printf("\n"); } (*pGraph)->arcNum /= 2; return OK; } // 深度优先遍历DFS Status DFS(Graph* pGraph, int* iVisitedArray, int visitedIndex) { if (pGraph == NULL) return ERROR; iVisitedArray[visitedIndex] = 1; printf("%c ", pGraph->vexs[visitedIndex]); for (int i = 0; i<pGraph->vexNum; i++) { if (pGraph->arcs[visitedIndex][i] > 0 && !iVisitedArray[i] && pGraph->arcs[visitedIndex][i] != MAX) DFS(pGraph, iVisitedArray, i); } return OK; } void getRecurShortestPath(Graph* pGraph, int* iShortArray, int vexIndex1, int vexIndex2){ if (*(iShortArray + (vexIndex1 * pGraph->vexNum) + vexIndex2) == -1) printf(" >> %c", pGraph->vexs[vexIndex2]); else { int iIndex = *(iShortArray + (vexIndex1 * pGraph->vexNum) + vexIndex2); getRecurShortestPath(pGraph, iShortArray, vexIndex1, iIndex); getRecurShortestPath(pGraph, iShortArray, iIndex, vexIndex2); } } void getFloyd(Graph* pGraph) { int i, j, k; int iShortArray[5][5] = { 0 }; // 记录最短路径 for (i = 0; i < pGraph->vexNum; i++) // 行的计数 { for (j = 0; j < pGraph->vexNum; j++) // 列的计数 { iShortArray[i][j] = -1; } } // 这里的k代表的加入了哪个节点之后, // 比如k=0的时候,就是加入顶点1之后的情况。 // k=1的时候,就是加入了顶点2之后的情况 // k=2的时候,就是加入了顶点3之后的情况,此时类推即可 for (k = 0; k < pGraph->vexNum; k++) { for (i = 0; i < pGraph->vexNum; i++) // 行 { for (j = 0; j<pGraph->vexNum; j++) // 列 { if (pGraph->arcs[i][j] >(pGraph->arcs[i][k] + pGraph->arcs[k][j])) { pGraph->arcs[i][j] = pGraph->arcs[i][k] + pGraph->arcs[k][j]; iShortArray[i][j] = k; } } } printf("===============加入顶点%d之后的情况=================\n", k + 1); for (int q = 0; q < pGraph->vexNum; q++) { for (int p = 0; p < pGraph->vexNum; p++) { printf("%d ", iShortArray[q][p]); } printf("\n"); } } printf("shortestPath: %c", pGraph->vexs[0]); getRecurShortestPath(pGraph, (int*)iShortArray, 0, 4); } int main() { int iVisited[5] = { 0 }; int initArcs[5][5] = { 0, 10, MAX, 30, 100, MAX, 0, 50, MAX, MAX, MAX, MAX, 0, MAX, 10, MAX, MAX, 20, 0, 60, MAX, MAX, MAX, MAX, 0 }; char initVexs[6] = "12345"; Graph* pGraph = initGraph(5); createGraph(&pGraph, initVexs, (int*)initArcs); getFloyd(pGraph); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南