最短路径问题-Dijkstra算法
有权单源最短路径问题
具体思路和图解看这个-https://www.cnblogs.com/ssyfj/p/9491895.html
知识点: 1)权值不可为负 2)Dijkstra算法解决了从某个顶点到其余各顶点的最短路径。其时间复杂度是O(n*2)
更详细的图解(一看就懂!!!):https://zhuanlan.zhihu.com/p/129373740
实例:https://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html
一个无向图
用Dijkstra算法找出以A为起点的单源最短路径步骤如下:
一:有权的单源最短路径算法(迪杰斯特拉Dijkstra算法)
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include "queue.h" #define MAXVEX 100 //最大顶点数 #define INFINITY 65535 //用0表示∞ typedef char VertexType; //顶点类型,字符型A,B,C,D... typedef int EdgeType; //边上权值类型10,15,... //邻接矩阵结构 typedef struct { VertexType vers[MAXVEX]; //顶点表 EdgeType arc[MAXVEX][MAXVEX]; //邻接矩阵,可看作边表 int numVertexes, numEdges; //图中当前的顶点数和边数 }MGraph; //创建邻接矩阵 void CreateMGraph(MGraph* G); //显示邻接矩阵 void showGraph(MGraph G); //迪卡斯特拉算法,获取最短路径 void Dijkatra(MGraph G, int s); void Dijkatra(MGraph G,int s) { int path[MAXVEX]; //是数组下标表示的顶点所经历的前一个顶点 int dist[MAXVEX]; //是数组下标表示的顶点的最小权值路径和 //上面两个数组都有作用,和无权最短路径一致,但是无权最短路径可以使用dist是否被设置来判断一个顶点是否被访问, //但是这里无法使用,因为dist和普里姆算法中的lowcost一样,是使用贪心算法时,每到一个顶点,我们都会全部更新dist //所以我们需要另外一个数组来标志各个顶点是否被访问 int final[MAXVEX]; int i,j,k,min; //对数据进行初始化 for (i = 0; i < G.numVertexes;i++) { final[i] = 0; //0表示该数组下标所表示的顶点未被访问 path[i] = 0; //初始化路径数组为0,表示当前每个都是独立的根节点 dist[i] = G.arc[s][i]; //这一步是重点:初始化路径数组的值为起始v0到各个点的权值 } dist[s] = 0; //到源点自己的路径为0 path[s] = s; //设置源点的前一个顶点就是自己 final[s] = 1; //源点被访问过了 //开始主循环,每次求的v0(s)到某个v顶点的最短路径 for (i = 0; i < G.numVertexes;i++) { min = INFINITY; //和普里姆算法相似 for (j = 0; j < G.numVertexes;j++) //由于是有向图所以都要从0开始,找到他的每个邻接点 { if (!final[j]&&dist[j]<min) //若是该顶点没有被访问过,且该点到s点的距离小于min,我们就将min设置为他 { k = j; //记录下该v到s点的下标和min最小路径 min = dist[j]; } } final[k] = 1; //将目前找到的距离v0(S)最近的顶点置为1 for (j = 0; j < G.numVertexes;j++) //修正当前最短路径即距离 { //修正方法就是循环k的每个邻接点,我们作为三角形来看,若是两边之和小于第三边,那我们原来找的那条直接的最短边就失效了,用这两条直接代替 //所以我们将距离修改,路径设置为他的上一步k, if (!final[j]&&(min+G.arc[k][j])<dist[j]) { //说明找到了更短的路径,修改dist和path数组 dist[j] = min + G.arc[k][j]; //修改当前路径长度 path[j] = k; } } } for (i = 0; i<G.numVertexes; i++) { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } } int main() { MGraph MG; CreateMGraph(&MG); showGraph(MG); Dijkatra(MG,0); system("pause"); return 0; } //生成邻接矩阵 void CreateMGraph(MGraph* G) { int i, j, k, w; G->numVertexes = 7; G->numEdges = 12; //读入顶点信息 G->vers[0] = 'A'; G->vers[1] = 'B'; G->vers[2] = 'C'; G->vers[3] = 'D'; G->vers[4] = 'E'; G->vers[5] = 'F'; G->vers[6] = 'G'; G->vers[7] = 'H'; G->vers[8] = 'I'; //getchar(); //可以获取回车符 for (i = 0; i < G->numVertexes; i++) for (j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; //邻接矩阵初始化 //创建了有向邻接矩阵 G->arc[0][1] = 2; G->arc[0][3] = 1; G->arc[1][3] = 3; G->arc[1][4] = 10; G->arc[2][0] = 4; G->arc[2][5] = 5; G->arc[3][2] = 2; G->arc[3][4] = 2; G->arc[3][5] = 8; G->arc[3][6] = 4; G->arc[4][6] = 6; G->arc[6][5] = 1; } //显示邻接矩阵 void showGraph(MGraph G) { for (int i = 0; i < G.numVertexes; i++) { for (int j = 0; j < G.numVertexes; j++) { if (G.arc[i][j] != INFINITY) printf("%5d", G.arc[i][j]); else printf(" 0"); } printf("\n"); } }
记忆方法: 假设10个节点 G【】【】: 0 1 2 3 4 5 A B C D E F 0 A 0 6 3 9 9 9 1 B 6 0 2 5 9 9 2 C 3 2 0 3 4 9 3 D 9 5 3 0 2 3 4 E 9 9 4 2 0 5 5 F 9 9 9 3 5 0 int dijkstra(Graph G) { 1:需要一个记录最短路径数值的数组dist【10】,一个记录是否访问节点的数组final【10】,
一个记录最短路径中当前节点前一个节点地址的数组path【10】; 2:dist【10】数组赋值-将G【0】赋值给dist【10】 for(i=0;i<10;i++) { dist[i]=G[0][i]; path[i]=0; final[i]=0; } 3:第一个节点不需要循环先写好数据dist[0]=0;final[0]=1; path[0]=0; 4:核心步骤 for(i=0;i<10;i++) { min=999;//假设为无穷 for(j=0;j<10;j++) { if(final[j]!=1&&dist[j]<min) { min=dist[j]; min_index=j; } } final[min_index]=1; for(j=0;j<10;j++) { int p=dist[min_index]+G[min_index][j]; if(final[j]!=1&&dist[j]>p) { dist[j]=p; path[j]=min_index; } } }
二:基于无向图的顶点加权Dijkstra算法
头文件及及结构
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAXVEX 100 #define INFINITY 65535 typedef char VertexType; typedef int EdgeType; typedef struct { VertexType vers[MAXVEX]; //顶点表 EdgeType arc[MAXVEX][MAXVEX]; //邻接矩阵 int numVertexes, numEdges; //图中的顶点数和边数 }MGraph; //创建邻接矩阵 void CreateMGraph(MGraph* G); //显示邻接矩阵 void showGraph(MGraph G); //迪杰斯特拉算法,获取最短路径 void Dijkatra(MGraph G, int s);
1)CreateMGraph(MGraph* G)
void CreateMGraph(MGraph* G) { int i, j; G->numVertexes = 7; G->numEdges = 12; //读入顶点信息 G->vers[0] = 'A'; G->vers[1] = 'B'; G->vers[2] = 'C'; G->vers[3] = 'D'; G->vers[4] = 'E'; G->vers[5] = 'F'; G->vers[6] = 'G'; G->vers[7] = 'H'; G->vers[8] = 'I'; for (i = 0; i < G->numVertexes; i++) for (j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; //邻接矩阵初始化 //创建了有向邻接矩阵 G->arc[0][1] = G->arc[1][0] = 2; G->arc[0][3] = G->arc[3][0] = 1; G->arc[1][3] = G->arc[3][1] = 3; G->arc[1][4] = G->arc[4][1] = 5; G->arc[2][0] = G->arc[0][2] = 4; G->arc[2][5] = G->arc[5][2] = 5; G->arc[3][2] = G->arc[2][3] = 2; G->arc[3][4] = G->arc[4][3] = 2; G->arc[3][5] = G->arc[5][3] = 8; G->arc[3][6] = G->arc[6][3] = 4; G->arc[4][6] = G->arc[6][4] = 2; G->arc[6][5] = G->arc[5][6] = 3; //读入顶点信息 for (i = 0; i < G->numVertexes; i++) G->arc[i][i] = 0; G->arc[1][1] = 4; G->arc[2][2] = 2; G->arc[3][3] = 10; G->arc[4][4] = 1; G->arc[5][5] = 3; }
2)showGraph(MGraph G)
void showGraph(MGraph G) { for (int i = 0; i < G.numVertexes; i++) { for (int j = 0; j < G.numVertexes; j++) printf("%6d", G.arc[i][j]); printf("\n"); } }
3)Dijkatra(MGraph G, int s)
void Dijkatra(MGraph G, int s) { int path[MAXVEX]; //是数组下标表示的顶点所经历的前一个顶点 int dist[MAXVEX]; //是数组下标表示的顶点的最小权值路径和 //上面两个数组都有作用,和无权最短路径一致,但是无权最短路径可以使用dist是否被设置来判断一个顶点是否被访问, //但是这里无法使用,因为dist和普里姆算法中的lowcost一样,是使用贪心算法时,每到一个顶点,我们都会全部更新dist //所以我们需要另外一个数组来标志各个顶点是否被访问 int final[MAXVEX]; int i, j, k, min; //对数据进行初始化 for (i = 0; i < G.numVertexes; i++) { final[i] = 0; //0表示该数组下标所表示的顶点未被访问 path[i] = 0; //初始化路径数组为0,表示当前每个都是独立的根节点 dist[i] = G.arc[s][i]; //这一步是重点:初始化路径数组的值为起始v0到各个点的权值 } dist[s] = 0; //到源点自己的路径为0 path[s] = s; //设置源点的前一个顶点就是自己 final[s] = 1; //源点被访问过了 //开始主循环,每次求的v0(s)到某个v顶点的最短路径----(找到距离源点s,并且没有被访问过的顶点的最近顶点) for (i = 0; i < G.numVertexes; i++) { min = INFINITY; //和普里姆算法相似 for (j = 0; j < G.numVertexes; j++) //由于是有向图所以都要从0开始,找到他的每个邻接点 { if (!final[j] && dist[j] < min) //若是该顶点没有被访问过,且该点到s点的距离小于min,我们就将min设置为他 { k = j; //记录下该v到s点的下标和min最小路径 min = dist[j]; } } final[k] = 1; //将目前找到的距离v0(S)最近的顶点置为1 for (j = 0; j < G.numVertexes; j++) //修正当前最短路径即距离 { //修正方法就是循环k的每个邻接点,我们作为三角形来看,若是两边之和小于第三边,那我们原来找的那条直接的最短边就失效了,用这两条直接代替 //所以我们将距离修改,路径设置为他的上一步k, if (!final[j] &&k!=j&&(min + G.arc[k][j]+G.arc[k][k]) < dist[j]) //开始加上了经过的顶点权值进行比较 { //说明找到了更短的路径,修改dist和path数组 dist[j] = min + G.arc[k][j] + G.arc[k][k]; //修改当前路径长度,是加上途经的顶点权值 path[j] = k; } } } for (i = 0; i < G.numVertexes; i++) { printf("%d %c-%c\n", dist[i], G.vers[path[i]], G.vers[i]); } }
4)
int main() { MGraph MG; CreateMGraph(&MG); showGraph(MG); Dijkatra(MG, 0); system("pause"); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?