迪杰斯特拉(Dijkstra)算法详解

【专栏】数据结构复习之路

这篇文章来自上述专栏中的一篇文章的节选:

【数据结构复习之路】图(严蔚敏版)两万余字&超详细讲解

想了解更多图论的知识,可以去看看本专栏 

Dijkstra 算法讲解:

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是:从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

这里我直接以书P189那个例子为基础进行讲解(附带书上的源代码)

先给出算法代码,然后结合着代码来讲可能会更容易理解:

/* 迪杰斯特拉(Dijkstra) 算法*/
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc path, ShortPathTable dist)
{
	int v, w, k, min;
	int final[MaxVerterNum];		// final[w] = 1表示求得顶点 v0 至 vw的最短路径,即已访问过顶点vw
	for (v = 0; v < G.vexNum; v++)
	{
		final[v] = 0;				// 全部顶点初始化为未知最短路径状态
		dist[v] = G.Edge[v0][v];	// 将与v0点有连线的顶点加上权值
		path[v] = -1;				// 初始化路劲数组p为-1
	}
	dist[0] = 0;	// v0至v0路径为0
	final[0] = 1;	// v0至v0不需要路径
	/* 开始主循环,每次求得v0到某个顶点v的最短路径*/
	for (v = 1; v < G.vexNum; v++)
	{
		min = INFINITY;		// 当前所知离v0顶点的最近距离
		for (w = 0; w < G.vexNum; w++)	// 寻找离v0最近的顶点
		{
			if (!final[w] && dist[w] < min)
			{
				k = w;
				min = dist[w];	// w顶点离v0顶点更近
			}
		}
		final[k] = 1;	// 将目前找到的最近的顶点置为1
		for (w = 0; w < G.vexNum; w++)	// 修正当前最短路径及距离
		{
			/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
			if (!final[w] && (min + G.Edge[k][w] < dist[w]))
			{
				dist[w] = min + G.Edge[k][w];	// 修改当前路径长度
				path[w] = k;
			}
		}
	}
}

【1】初始化(执行上述代码前面一部分)

⚠️注意:这里的path[2] 、path[4]、path[5]没有初始化为0,主要是没必要,因为path[i] = -1,就表明 i 的前驱结点一定就是V0。

  • final[i]:标记各顶点是否已找到最短路径
  • dist[i]:最短路径长度
  • path[i]:路径上的前驱
    int v, w, k, min;
	int final[MaxVerterNum];  // final[w] = 1表示求得顶点 v0 至 vw的最短路径,即已访问过顶点vw
	for (v = 0; v < G.vexNum; v++)
	{
		final[v] = 0;		// 全部顶点初始化为未知最短路径状态
		dist[v] = G.Edge[v0][v]; // 将与v0点有连线的顶点加上权值
		path[v] = -1;		// 初始化路劲数组p为-1
	}
	dist[0] = 0;			// v0至v0路径为0
	final[0] = 1;			// v0至v0不需要路径

【2】找到距离V0最近的顶点,并修改当前路径长度 

/* 开始主循环,每次求得v0到某个顶点v的最短路径*/
	for (v = 1; v < G.vexNum; v++)
	{
		min = INFINITY;		// 当前所知离v0顶点的最近距离
		for (w = 0; w < G.vexNum; w++)	// 寻找离v0最近的顶点
		{
			if (!final[w] && dist[w] < min)
			{
				k = w;
				min = dist[w];	// w顶点离v0顶点更近
			}
		}
		final[k] = 1;			// 将目前找到的最近的顶点置为1
		for (w = 0; w < G.vexNum; w++)	// 修正当前最短路径及距离
		{
			/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
			if (!final[w] && (min + G.Edge[k][w] < dist[w]))
			{
				dist[w] = min + G.Edge[k][w];	// 修改当前路径长度
				path[w] = k;
			}
		}
	}

【3】 重复【2】 

​ 【4】重复【2】

【5】重复【2】


到这里,dist[i]里面存的就是从V0到 Vi 的最短路径长度,而通过path[i] 就能找到最短路径。

这里V1至始至终都没有更新的原因是:V0根本走不到V1。

看完上述图解,那么书上P190那个表格你肯定就明了:

下图的S相当于 final[i]依次被确定为1的次序

 这个表格建议大家要搞懂!可能有些学校会考察画图哦

⚠️注意:

迪杰斯特拉算法适用于求正权有向图中,源点到其余各个节点的最短路径。(图中可以有环,但不能有负权边)。

例如:如下图就不能使用迪杰斯特拉算法求节点 1 到其余各个节点的最短距离。

因为根据迪杰斯特拉算法,首先会更新dist[2] = 1 , final[2] = 1。由于final[2]被确定为1,即之后将不会再更新dist[2],而根据上图,显然结点1到结点2的最短路径为-1。


显然,Dijkstra 算法是基于贪心策略的。使用邻接矩阵或者带权的邻接表表示时,时间复杂度都是:O(|V|^{2})

这里的V是图里面的结点个数。
人们可能只希望找到从源点到某个特定顶点的最短路径,但这个问题和求解源点到其他所有顶点的最短路径一样复杂,时间复杂度也为O(|V|^{2})​ 


 我的个人博客,欢迎访问!

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
以下是狄克斯特拉算法的C语言代码,使用邻接矩阵表示图: ```c #include <stdio.h> #include <limits.h> #define MAX_VERTICES 100 #define INF INT_MAX int graph[MAX_VERTICES][MAX_VERTICES]; // 邻接矩阵表示图 int dist[MAX_VERTICES]; // 存储最短路径 int visited[MAX_VERTICES]; // 记录节点是否已经被访问过 int dijkstra(int start, int end, int n) { // 初始化 for (int i = 0; i < n; i++) { dist[i] = INF; visited[i] = 0; } dist[start] = 0; for (int i = 0; i < n; i++) { // 找到未访问节点中距离起点最近的节点 int min_dist = INF; int u = -1; for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; u = j; } } if (u == -1) break; visited[u] = 1; // 更新与该节点相邻的节点的最短路径 for (int v = 0; v < n; v++) { if (!visited[v] && graph[u][v] != INF && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } return dist[end]; } int main() { int n, m; scanf("%d %d", &n, &m); // 初始化邻接矩阵 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) graph[i][j] = 0; else graph[i][j] = INF; } } // 读入边 for (int i = 0; i < m; i++) { int u, v, w; scanf("%d %d %d", &u, &v, &w); graph[u][v] = w; graph[v][u] = w; } int start, end; scanf("%d %d", &start, &end); int shortest_path = dijkstra(start, end, n); printf("%d\n", shortest_path); return 0; } ``` 在这个代码中,我们使用了邻接矩阵来表示图,其中 `graph[i][j]` 表示节点 `i` 和节点 `j` 之间的边权值。我们使用了 `dist` 数组来存储起点到各个节点的最短路径长度,使用 `visited` 数组来记录节点是否已经被访问过。算法的具体实现方法与伪代码相同,这里不再赘述。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吹往北方的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值