在考研中,图的应用部分有四个大考点分别为最小生成树、最短路径问题、拓扑排序以及关键路径。在最短路径问题中有两个小考点分别为Dijkstra算法和Floyd算法。在本文,将对Dijkstra算法进行知识总结、讲解以及c++代码呈现。
目录
《数据结构与算法》——Dijkstra算法总结
目的
要求
思想
手动实现
时间复杂度
代码实现
参考文献
求一个带权有向图中某个定点到其他各个顶点的最短路径,即解决单源图的最短路径问题。
举个例子,假如你现在正在乘坐泰坦尼克号在广袤的大海上旅行,突然船撞倒了冰山上(从现在开始,剧情变化了),所有的人都在逃生。你现在有幸跳到了一艘救生艇上,现在救生艇下面漂着100个人,你和这100个人之间或多或少的都有些联系,救生艇能将你们所有人救出来,但救生艇每次只能上一个人,在忽略人员性别等问题,单纯从和你相关关系的角度出发的情况下,请问怎么救人?
在题目中“你”是一个只看私人感情的人,毋庸置疑,得先救出和自己关系最好的人小A,然后再从{自己与水中人的关系,自己与小A的关系+小A与水中人的关系}中进行比较选出最好关系对,并由其去救起小B,下面类似的从{自己与水中人的关系,自己与小A的关系+小A与水中人的关系,自己与小A的关系+小A与小B的关系+小B与水中人的关系,自己与小B的关系+小B与水中人的关系}中进行比较选出最好的关系对,并由其救起小C,以此类推,直至将所有落水者救出为止。(注:本例子只用作辅助理解,并无其他意思。)
在救人的过程中,我们需要两个名单作为记录,一个用于记录各个落水者与“你”的关系程度,一个用于记录救起各个落水者的人员。
用算法的语言来描述如下:
arcs[][]:记录各个顶点到其它顶点的直接长度。(“你”和各个落水者的直接关系)
s[]:用于记录已求得的最短路径的节点。(被救起的落水者)
v[]:为原始的顶点集合。
path[]:记录源点到各个节点的最短路径的前驱节点(救起各个落水者的人员),初始值为arcs[v0][i](和“你”的关系)。
dist[]:记录源点到各个节点的最短路径长度。(关系)
假设从v0出发,初始化:s={v0},dist[i]=arcs[v0][i],path[i] = v0,i为集合v剩余节点,v=v-s(在集合v中去掉初始点)
while(v集合不为空){
从集合v中选出一个顶点vj,它满足dist[vj] = Min{dist[vi],vi属于v},此点即为从v0出发的一条最短路径的终s=s+{vj} v=v-{vj}
如果是第一趟选出的点,则令path[vj] = v0
修改最短路径dist ,对于v中的任意一个顶点vk,若dist[vk] > dist[vj] + arcs[vj][vk] , 则令dist[vk]=dist[vj]+arcs[vj][vk],path[vk] = vj
}
以2016年计算机联考真题为例,
如下图
从顶点1出发,初始化
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
无穷 |
无穷 |
4 |
无穷 |
path |
-1 |
1 |
-1 |
-1 |
1 |
-1 |
距离最短的是的<1,5>,将点5添加到s中;修改dist值。
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
无穷 |
11 |
4 |
9 |
path |
-1 |
1 |
-1 |
5 |
1 |
5 |
经计算比较发dist最小的是<1,2>,将点2添加到s中,修改dist值。
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
7 |
11 |
4 |
9 |
path |
-1 |
1 |
2 |
5 |
1 |
5 |
经计算比较发dist最小的是<2,3>,将点3添加到s中,修改dist值。
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
7 |
11 |
4 |
9 |
path |
-1 |
1 |
2 |
5 |
1 |
5 |
经计算比较发dist最小的是<5,6>,将点6添加到s中,修改dist值。
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
7 |
11 |
4 |
9 |
path |
-1 |
1 |
2 |
5 |
1 |
5 |
经计算比较发dist最小的是<5,4>,将点4添加到s中,修改dist值。
|
1 |
2 |
3 |
4 |
5 |
6 |
dist |
0 |
5 |
7 |
11 |
4 |
9 |
path |
-1 |
1 |
2 |
5 |
1 |
5 |
计算结束。s序列为1 5 2 3 6 4
当然考试的时候没必要将所有的步骤写出来,要做到又快又准。
本算法是基于贪心策略的。算法的主要部分是由双重循环组成的,第一层循环每次求出一个符合要求的点及其最短路径,内层循环是进行遍历以查找最短路径,其时间复杂度为o(|v|^2),其中|v|为所有顶点数。当把各个点都看成顶点进行算法执行时,需要执行|v|次算法,则此时算法的时间复杂度为 o(|v|^3)。
最近有些朋友在搞数模用到了这个算法,自己就顺带先复习一下这个算法。
/*编译环境:
win10专业版
DEV C++ 5.11
TDM-GCC 4.9.2 64bit
*/
#include
#include
#include
#define INF 65535
#define Max 5
using namespace std;
int pp[(Max+1)*3] ;
void Dijkstra(int arcs[][Max+1],int n,int v0){
int path[n+1] ;
int dist[n+1] ;//此两项无需初始化
int s[n+1] = {2};//第一项初始化,s[0]记录旧集合开始的下标
int i,j,k,min = INF,temp,v1;
bool change=1;//标记,防止图不连通形成死循环
for(i=1;i<=n;i++){//dist和s赋初值
dist[i] = arcs[v0][i];
s[i] = i ;
//cout<dist[temp]+arcs[temp][s[i]]){
dist[s[i]] = dist[temp]+arcs[temp][s[i]] ;
path[s[i]] = temp;
}
i++;//if
}//while
v1 = temp;
}//while
for(i = 1;i<=n;i++){
pp[i] = path[i];
pp[i+n] = dist[i];
pp[i+2*n] = s[i];
}
return ;
}//fun
int main(){
//变量声明
int arcs[Max+1][Max+1];// = {{INF}};//初始化
int v0;
int n;
stack ss;
//int *pp;// [2][Max+1]
//初始化
for(int i = 0;i<=Max;i++)
for(int j = 0; j<=Max; j++)
arcs[i][j] = INF;
arcs[1][2] = 10;arcs[1][5] = 5 ;
arcs[2][3] = 1 ;arcs[2][5] = 2 ;
arcs[3][4] = 6 ;arcs[4][1] = 7 ;
arcs[4][3] = 6 ;arcs[5][2] = 3 ;
arcs[5][3] = 9 ;arcs[5][4] = 2 ;
n = 5;
v0 = 1;
//执行算法
Dijkstra(arcs,n,v0);
//输出
//插入顺序队列
cout<<"集合S:\t";
for(int i = 1;i<=n;i++)
cout<
如有错误,还请各位不吝指正。