Dijkstra最短路径算法详解
来源:互联网 发布:mac风扇一直转 编辑:程序博客网 时间:2024/05/19 16:20
简介
Dijkstra最短路径算法是非常经典的图搜索算法,而且具有一定难度,需要花时间好好理解。算法导论第24章对此有详细的分析,值得研究一番。而我自己也结合一个具体实例,实现了代码,确实只有在编码的过程中才能理解算法的细节,提高自己的算法能力。
实例分析
1.分步解析
Dijkstra最短路径算法也叫单源最短路径算法,意思就是只需要输入一个起点,求出该起点到图中其余点的最短路径即可。(而floyd则是求任意两点间的最短路径)
假如有如下有向图(无向图也可以,不能存在负值的边):
依旧使用二维数组存储这幅图的点和边:
还需要一个distance数组,用于存储起点到其余终点的最短距离:
假设我们的起点即为1号顶点,由上图可知,此时它只有到点2,点3两条路。要找点1到剩余2,3,4,5,6号顶点的最短距离,那么当然只能先到点2,或点3,再到4,5,6号点。而应该通过走边(1–>2) 还是走边 (1–>3)再走到其他的点呢?相信正常人都会走最短的那条,也就是边(1–>2)。
也就是说,此时,我们要距离起点1,距离最近的点作为中转点,也就是2号点,将它加入到最短路径记录数组中。
既然选择了走2号点,那么从2号点出发,有两条路可走,先讨论通过(2–>3)这条边能否让1号顶点到3号顶点的路程变短。也就是说现在来比较distance[3]和distance[2]+e[2][3]的大小。其中distance[3]表示1号顶点到3号顶点的路程。distance[2]+e[2][3]中distance[2]表示1号顶点到2号顶点的路程,e[2][3]表示(2–>3)这条边。所以distance[2]+e[2][3]就表示从1号顶点先到2号顶点,再通过2->3这条边,到达3号顶点的路程。
我们发现原来的distance[3]=12,而distance[2]+e[2][3]=1+9=10,distance[3]>distance[2]+e[2][3],因此dis[3]要更新为10。这个过程在算法导论中的专业术语叫做边的“松弛”。1号顶点到3号顶点的路程即distance[3],通过2->3这条边松弛成功。这便是Dijkstra算法的主要思想:通过“边”来松弛1号顶点到其余各个顶点的路程。
同理,通过边(2–>4)(即e[2][4]),可以将distance[4]的值从∞松弛为4(distance[4]初始为∞,distance[2]+e[2][4]=1+3=4,distacne[4]>distance[2]+e[2][4],因此dis[4]要更新为4)。
此时,对2号顶点所有的出边进行了松弛。松弛完毕之后distance数组为:
因为2号顶点加入了最短路径中转记录数组中,所以继续找剩下的3,4,5,6号顶点,找出它们之中距离1号起点最近的点。显然,此时点4距离起点最近,所以把点4加入最短路径中转记录数组,然后对点4的所有出边(4->3,4->5和4->6)进行松弛操作。
松弛完毕后最短路径数组变化为:
继续在剩下的3,5,6号点中寻找离起点距离最近的顶点作为中转点,这次找到3号。对3号顶点的所有出边(3->5)进行松弛后:
继续在剩下的5和6号顶点中,选出离1号顶点最近的顶点,这次选择5号顶点,对5号顶点的所有出边(5->4)进行松弛:
最后只剩下6号点了,由于6号点没有出边,所以就不用松弛了。
2.代码实现:
//创建图void createGraph(int graph[][POINTS]) { int mid_tmp, points_num, edges_num; //获取点数 & 边数 scanf("%d %d",&points_num,&edges_num); //全部初始化为正无穷 for (int i = 1;i <= points_num;++i) for (int j = 1;j <= points_num;++j) { if (i == j) graph[i][j] = 0; else graph[i][j] = INF; } //输入边 int m,n,length; for (int i = 1;i <= edges_num;++i) { scanf("%d %d %d",&m,&n,&length); graph[m][n] = length; }}
void showGraph(int graph[][POINTS],int n) { for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) { printf("%10d",graph[i][j]); } printf("\n"); }}
/单源最短路径Dijkstra算法void dijkstra(int graph[][POINTS],int points,int start_point) { int distance[POINTS], //从起点到其他顶点的最短距离数组 record[POINTS]; //新加入中转顶点记录数组 //Stpe 1:初始化工作 memset(distance,INF,sizeof(distance)); for (int i = 1;i<points; ++i) distance[i] = graph[start_point][i]; //up^: 一开始只有与起点联通的点有距离,其余均不可到达 for (int i = 1;i<points; ++i) record[i] = 0; record[start_point] = 1; //开始时只有起点加入标记集合中 //***最短路径主循环***// int min,min_mid_ponit; for (int i = 1; i<= points - 1; ++i) { //找剩余的n-1个点 //Step 2:寻找离1号顶点距离最近的中转点,不断加入到标记集合中 min = INF; for (int j = 1; j <= points; ++j) { if (record[j] == 0 && distance[j] < min) { min = distance[j]; min_mid_ponit = j; } } record[min_mid_ponit] = 1; //up^:找到此时距离起点距离最近的点,将该点加入已知集合中 //Step 3:通过新加入的最近中转点,更新起点到其余各点的最短路径 int end_ponit; for( end_ponit = 1; end_ponit <= points; ++end_ponit ) { //最近中转点到终点有路可达 if ( graph[min_mid_ponit][end_ponit] < INF ) { if ( distance[end_ponit] > distance[min_mid_ponit] + graph[min_mid_ponit][end_ponit] ) distance[end_ponit] = distance[min_mid_ponit]+graph[min_mid_ponit][end_ponit]; //up^:更新最短路径 } } } //输出最终的结果 cout<<"从起点"<<start_point<<"到其余各点的最短距离为:"<<endl; for( int i=1;i <= points; ++i ) printf("%d ",distance[i]);}
主函数
int main(void) { int graph[POINTS][POINTS]; createGraph(graph); showGraph(graph,6); //floyd(graph,4); dijkstra(graph,6,1); system("pause"); return 0;}
运行结果:
Ps:Dijkstra算法还可以用priority_queue或者斐波那契堆来进行优化。
- Dijkstra最短路径算法详解
- 最短路径问题---Dijkstra算法详解
- 最短路径问题---Dijkstra算法详解
- DIJKSTRA最短路径算法
- 最短路径算法-dijkstra
- dijkstra最短路径算法
- 最短路径 Dijkstra算法
- 最短路径(Dijkstra算法)
- 最短路径Dijkstra算法
- 最短路径 Dijkstra算法
- Dijkstra最短路径算法
- 最短路径dijkstra算法
- 最短路径 dijkstra算法
- 最短路径Dijkstra 算法
- 最短路径 (Dijkstra算法)
- Dijkstra最短路径算法
- 最短路径(Dijkstra算法)
- 最短路径--Dijkstra算法
- 二叉树题目
- 机器学习入门-了解相关概念
- SDM For Face Alignment流程介绍及Matlab代码实现之测试篇
- 面向对象笔记
- 使用PullToRefresh实现下拉刷新和上拉加载
- Dijkstra最短路径算法详解
- 双链表:实现基本的增删查改,正反向现实双链表的节点
- 垃圾回收机制与内存分配策略
- 黑马程序员---指向函数的指针与返回指针的函数
- 黑马程序员---c语言基础---数组、指针
- java学习笔记——static关键字
- Ruby On Rails 快速创建项目
- HDU 5344 多个数的和异或-思维-(位运算)
- 读 HTTP 协议