Dijkstra算法基础入门

[TOC]

前言

Dijkstra是一种寻找最短路径的算法,虽然思维容易理解,但入门稍有难度。为了方便以后理解,于是写下这篇文章。若文章含有错误,欢迎各位纠正QwQ


1.Dijkstra的C代码

为了方便大家复制,就先把代码放这了

//#define INF    0x7f7f7f7f
//#define MAXSIZE 1000;
//int map[MAXSIZE][MAXSIZE], dp[MAXSIZE], used[MAXSIZE];
//INF:int型最大值
//map:存储各个节点之间的距离
//dp:存储当前最短路径
//num:所有节点个数
//used:记录是否建立了最短路径
//index:访问节点的索引值
void Dijkstra () {
int i, j ;
int min, index;
memset (used, 0, sizeof (used)) ;
for (i = 1 ; i <= num ; ++i) dp[i] = map[0][i] ;
for (i = 1 ; i <= num ; ++i) {
min = INF ;
index = 0 ;
for (j = 1 ; j <= num ; ++j)
if (used[j]==0&& dp[j] < min){
index = j ;
min = dp[j] ;
}
used[index] = 1 ;
for (j = 1 ; j <= num ; ++j)
if (used[j]==0&& dp[index] + map[index][j] < dp[j])
dp[j] = dp[index] + map[index][j] ;
}
}

2.Dijkstra核心解析

1.构建map(对数据结构了解可直接跳过)

假如我们得到如下一堆数据(各个节点之间的距离)

A↔D:6

A↔B:3

B↔D:2

B↔E:5

B↔C:3

E↔F:5

将数据化成如下二叉树

从图中可以看出A与A之间的距离显然为0,A与B、D相连,距离分别是3、6,但C、E、F均不与A相连,为了能够表示其他节点不与A相连,于是我们不妨将A与不相连节点之间的距离设为∞,(即为代码中的INF),则得到如下一组数据

0	3	INF	6	INF	INF	

同样我们分别对其他节点分析,可得到另外几组数据,将这几组数据汇聚在一起,就是map(如下表格)

0 3 INF 6 INF INF
3 0 3 2 4 INF
INF 3 0 INF INF INF
6 2 INF 0 INF INF
INF 4 INF INF 0 5
INF INF INF INF 5 0

2.更新dp[i]

假设我们要求某节点与目标节点的dp[i](在遍历过的节点中取得的最短的路径),总共有两种情况,第一种是与[已经确定的最短路径]==(遍历完所有节点都不会再找到更短的路径)==的节点直接相连,第二种是直接与目标节点相连。

这时可能有人发出疑问:假如是有多条路径都比节点直接与目标节点相连的距离要短该怎么求?(如下图)

其实解决方法很简单,因为dp中存储的就是[当前最短的路径],已经包含了第二种情况,我们只要将dp中存储的[当前最短路径]和第一种情况的路径相比就行了,哪个路径更短,则取较短的路径为dp[i],给出以下公式

dp[i]=min{dp[i],dp[m]+map[m][i]}
注:m为已确定[最短路径]的节点。

3.确定最短路径

既然知道怎么求[当前最短路径],那么如何将该节点的最短路径确定下来?解决方法就是[按最短路径从小到大来确定],从求[当前最短路径]可以看出影响该节点的只有比它更短路径的节点(之前图中的红、黄、蓝节点的路径均比紫色节点的路径短),给出以下结论

如果dp中未确定的路径都不小于它的路径,那么此时该节点的最短路径则确定下来

3.Dijkstra动态过程

首先遍历到B节点,B节点满足[未确定最短路径]和[小于min]两个条件,将3赋值给min

之后遍历到D节点时,虽然满足[未确定最短路径],但min此时的值为3,即不满足[小于min]这个条件,故跳过D点

最后遍历C、E、F节点时,同样发现不满足[小于min]这个条件,对全过程进行分析,最后筛选得到B节点符合条件,故把它与A之间的路径确定为最短路径(如果不懂,可以回到上面看结论)

每当有一个最短路径被确定,我们就需要更新一次dp

重新寻找符合两个条件的节点,确认最短路径,然后更新dp,重复此步骤。(先确定C还是D无所谓,因为两者在当前的最短路径相等)

4.Dijkstra算法实战

题目链接:zzulioj1263:一个人的旅行

样例输入

6 2 3
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2
8 9 10

样例输出

9

Dijkstra算法参考代码

#include <stdio.h> 
#include <string.h>
#include <stdlib.h>

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))

#define infinity 0x7f7f7f7f
#define minus_inf 0x80808080

#define MAXSIZE 1009

int road, neigh, dest, cityNum, minTime ;
int map[MAXSIZE][MAXSIZE], dp[MAXSIZE], used[MAXSIZE], want[MAXSIZE] ;

void dijkstra ()
{
int i, j ;
int minPath, iminPath ;
memset (used, 0, sizeof (used)) ;
for (i = 1 ; i <= cityNum ; ++i) dp[i] = map[0][i] ;
for (i = 1 ; i <= cityNum ; ++i) {
minPath = infinity ;
iminPath = 0 ;
for (j = 1 ; j <= cityNum ; ++j)
if (!used[j] && dp[j] < minPath) {
iminPath = j ;
minPath = dp[j] ;
}
used[iminPath] = 1 ;
for (j = 1 ; j <= cityNum ; ++j) if (!used[j] && dp[iminPath] + map[iminPath][j] < dp[j]) dp[j] = dp[iminPath] + map[iminPath][j] ;
}
minTime = dp[want[1]] ;
for (i = 2 ; i <= dest ; ++i) minTime = min (minTime, dp[want[i]]) ;
}

int main ()
{
int i, j ;
int a, b, c ;
while (scanf ("%d%d%d", &road, &neigh, &dest) != EOF){
memset (map, infinity, sizeof (map)) ;
cityNum = 0 ;
for (i = 1 ; i <= road ; ++i){
scanf ("%d%d%d", &a, &b, &c) ;
map[a][b] = map[b][a] = c ;
cityNum = max (cityNum, max (a, b)) ;
}
for (i = 1 ; i <= neigh ; ++i){
scanf ("%d", &a) ;
map[0][a] = map[a][0] = 0 ;
}
//以上步骤实现了构建map
for (i = 1 ; i <= dest ; ++i)
scanf ("%d", &want[i]) ;
dijkstra () ;
printf ("%d\n", minTime) ;
}
return 0 ;
}

二、参考相关文章链接:

1、Dijkstra算法详解 通俗易懂