Dijkstra算法
一、Dijkstra算法简介
Dijkstra算法是解决单源最短路径问题的贪心算法
该算法在计算的时候将所有的点分为两个集合,一个是目标点集U,初始时只有起点,Dijkstra算法的功能是,给定一个起点,计算它到其他所有点的最短路径
二、Dijkstra算法的基本思想
- 设置两点顶点的集合U和T,集合U中存放已找到最短路径的顶点,集合T 中存放当前还未找到的最短路径的顶点.
- 初始状态时,集合U中只包含源点,设为v0。
- 然后从集合T 中选择到源点v0路径长度最短的顶点u加入到集合U中
- 集合U中每加入一个新的顶点u都要修改源点带集合T 中剩余项点的当前最短路径值,集合T 中各项点的新的当前最短路径长度值,为 原来的当前最短路径长度值 与 从源点过顶点u到达该顶点的带权路径长度 中的较小者。
- 回到3,此过程不断重复,直到集合T中的顶点全部加入到集合U中为止。
三、图片例子
(一)例子1
看那个路线少加入那个,注意分辨虚线和实线
上述例子中,有图中圆圈中的字母代表节点的信息,圆圈上面的内容代表源点到其最短距离以及前驱节点,前驱节点用于后续重现最短路径。
这里主要是使用到了一个简单的定理,如果起点v0到目标点u之间的某一条路径是最短路径,那么在该路径上面,任何一个点u到的路径都是u'到v的最短路径。这个证明十分简单,使用反证法即可。
那么这里在记录最短路径的信息时,如果需要重现路径中的顶点,那么只需要对于每一个结点设置一个前驱结点即可。
(二)例子2
这里使用表格的形式来对整个算法流程进行分析。
四、实现
(一)代码
#include<cstdio> using namespace std; #define MAX 10000 #define ING 20000 int n, e; //顶点数n,边的条数e int cost[20][20]; //临界矩阵 int dist[20]; //存储最短路径的长度值 int pre[20]; //存储一个顶点在其最短路径的前趋 int S[20]; //标志数组,若为已经找到最短路径的结点则为1,否则为0 void Dijkstra(int v) { for (int i = 0; i < n; i++) { dist[i] = cost[v][i]; //初始化 S[i] = 0; //标志位初始为0 if (dist[i] < MAX) { pre[i] = v; //若存在边,则前趋为原点 } else { pre[i] = -1; //否则,前趋为-1,不可达 } } S[0] = 1; //原点标志为1 for (int i = 0; i < n - 1; i++) { //循环n-1次 int u; //u为待选顶点 int min = ING; //令初始最小值>max,使距离值为max的顶点也能加到S中 for (int j = 0; j < n; j++) { if ((!S[j]) && dist[j] < min) { //寻找距离S最小的顶点u min = dist[j]; u = j; } } S[u] = 1; //将其标志设置为1 for (int k = 0; k < n; k++) { //调整未加入S的点的的距离值 if ((!S[k]) && dist[k] > dist[u] + cost[u][k]) { dist[k] = dist[u] + cost[u][k]; pre[k] = u; //若通过u减小了k的距离,则修改k的前趋为u } } } printf("\nThe result:\n"); //输出结果 for (int i = 0; i < n; i++) { printf("<%d,%d>: ", v + 1, i + 1); int p = pre[i]; if (p != -1) { //若可达输出最短路径 printf("%d ", dist[i]); //输出最短距离 printf("%d", i + 1); //根据前趋逆向输出最短路径 while (p != v) { printf("<--%d", p + 1); p = pre[p]; } printf("<--%d", v + 1); } else { //若不可达则输出“inf” printf("inf"); } printf("\n"); } } int main() { printf("Please enter the number of n and e:"); scanf_s("%d%d", &n, &e); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cost[i][j] = MAX; //初始化为max } } int start, end, dut; for (int i = 0; i < e; i++) { printf("Please enter the number of start, end, dut:"); scanf_s("%d%d%d", &start, &end, &dut); //输入边的始点,终点和权值 start--; end--; //结点号与存储的下标相差1 cost[start][end] = dut; } int v = 0; Dijkstra(v); //以顶点1(即下标为0)为原点v return 0; }
(二)时间复杂度
V-节点个数,E-边个数
Dijkstra算法的总运行时间依赖于最小优先队列的实现。
1、数组 —— O( V2 + E ) —— O( V2 )
2、二叉堆 —— O( (V+E)logV )
3、斐波那契堆 —— O( VlogV + E )
五、补充
该算法只能计算边的权重值大于0的情况,对于边权值小于0的一些情况,该算法无法进行正确计算
能正确进行计算:
不能正确计算:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了