最短路径之Dijkstra算法(数据结构)

Dijkstra算法的产生背景

Edsger Wybe Dijkstra ,著名的荷兰计算机科学家,计算机科学史上最具影响力的人物之一,于1956年构思出了当今世界最经典的算法之一:Dijkstra算法

1956年,Dijkstra 在荷兰数学中心工作,当时他接到了来自荷兰航空公司的需求:帮助该公司规划最优航线。接到该需求以后的某一天,Dijkstra 与他的未婚妻在街上购物,走累了就在一家咖啡馆休息,随即,老爷子(当时还是年轻人)便灵光乍现,仅用20分钟便思考出了后来大名鼎鼎的Dijkstra单源最短路径算法

One morning I was shopping in Amsterdam with my young fiancee, and tired, we sat down on the cafe terrace to drink a cup of coffee. I was just thinking about whether I could do this, and I then designed the algorithm for the shortest path. As I said, it was a 20-minute invention.

------Edsger Dijkstra

这让人不得不感慨,天才跟普通人的差距竟能如此之大...


Dijkstra算法的概念

了解完历史背景后,各位肯定会好奇Dijkstra算法究竟有什么魔力,为什么会在计算机科学里具有如此高的地位?别急,在研究Dijkstra算法前,我们先来了解下面几个概念

源点

源点(Source) ,就是一条路径起始的第一个顶点,并且称最后一个顶点为终点(Destination) ,例如一条路径:V0 ->V1 ->V2 ->V3 ->V4,对整条路径 ,源点就是V0,终点就是V4。

需要注意的是,刚才的例子里着重强调了对整条路径,这是因为对不同路径,源点和终点不是固定的,比如对路径V1 ->V2 ->V3,显然源点是V1,终点是V3,也就是说,只有对给定的一条路径 而言,源点和终点是确定的。 当路径V1 ->V2 ->V3是路径V0 ->V1 ->V2 ->V3 ->V4的子路径 时,整条路径的源点是V0。在从V1出发遍历寻找离源点(也就是V0) 最近的点时,可以称呼V1为起点

最短路径

最短路径,那肯定就是最短的路径咯(bushi)。更准确的定义是,对无向图而言 ,从顶点Vi 到顶点Vj 所包含的边最少 的路径叫做从源点Vi终点Vj最短路径那这个最短路径怎么求呢?哎,这时候Dijkstra算法就派上用场了

Dijkstra算法

Dijkstra算法 用于求解单源最短路径 问题,也就是找出从一个连通图边权必须非负 )中指定源点出发到图中其他所有顶点的最短距离,该算法所运用到的核心思想是"贪心 策略",具体的实现逻辑如下:

1.源点出发,此时我们确定从源点到起点的距离是0(源点和起点不是一个东西!源点和起点不是一个东西!源点和起点不是一个东西!)

2. 每次从所有未知最短距离的顶点中挑选一个离源点最近的顶点

3. 一旦选中该点,就认为找到了到该点的最短路径,然后以该点为新的起点 去更新与它相邻的顶点的距离信息

4. 重复第2个和第3个操作,直到所有点 都被处理后结束

对该算法的代码实现,我们要明确几个数组的用法:

dist数组:用于记录起点到各个点的当前最短距离,初始化为极大值

visited数组:用于标记哪些点的最短路径已经确定,初始化为false

pre 数组:用来存储最短路径的具体走法,pre[i] = j 表示在从源点顶点 i 的最短路径上,顶点 i 的上一个前驱节点是顶点 j,初始化为-1

下面我们来看具体的代码实现(C++):

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//定义一个无穷大值 
const int INF = 0x3f3f3f3f;

//定义变量
int n,m;//顶点数 边数
vector<int> dist;//记录源点到各顶点的最短距离 
vector<bool> visited;//标记是否已确定最短路径 
vector<int> pre;//记录路径的前驱节点 

//邻接边表存图
vector<vector<pair<int,int>>> Graph;

//dijkstra实现函数 
void dijkstra(int source){
	//初始化
	dist.assign(n,INF);//初始化所有距离为无穷大
	visited.assign(n,false);//初始化所有顶点为未访问状态
	pre.assign(n,-1);//初始化前驱节点为-1 
	
	//源点到起点的距离为0
	dist[source] = 0;
	
	//循环遍历直到所有顶点都被处理
	for(int i=0;i<n;i++){
		//从所有为访问的点中找出dist最小的顶点
		int u = -1;//tmp存储dist最小的点的信息 
		int min_dist = INF;
		
		//遍历所有未访问的节点 
		for(int j=0;j<n;j++){
			if(!visited[j] && dist[j] < min_dist){
				u = j;
				min_dist = dist[j];
			}
		}
		
		if(u == -1)break;//若图不是连通图,直接退出
		
		visited[u]=true;//标记该顶点已访问
		
		//遍历顶点u的相邻顶点 
		for(int k=0; k<Graph[u].size(); k++){
			int v = Graph[u][k].first;//顶点u的相邻顶点
			int w = Graph[u][k].second;//边权
			
			//松弛操作:发现并更新最短路径的操作
			if(dist[u] + w < dist[v]){
				dist[v] = dist[u] + w;//更新最短距离 
				pre[v] = u;//记录前驱,即顶点v的前驱节点是顶点u 
			}	 
		}
	} 
}

int main(){
	cin>>n>>m;//顶点数 边数
	
	Graph.resize(n);//更新图的顶点个数 
	
	//初始化边
	for(int i=0;i<m;i++){
		int x,y,w;
		cin>>x>>y>>w;
		
		//因为是无向图,所以添加双向边
		Graph[x].push_back({y,w});
		Graph[y].push_back({x,w}); 	
	} 
	
	int source;//源点
	cin>>source;
	
	dijkstra(source); 
	
	//输出结果
	for(int i=0;i<n;i++){
		if(dist[i] == INF){
			cout<<source<<"无法到达"<<i<<endl;
		}
		else{
			cout<<source<<"到"<<i<<"的最短距离为:"<<dist[i]<<",路径是";
			
			//利用pre数组回溯路径
			vector<int> path;//保存最短路径的倒序 
			
			int p = i;
			while(p != -1){
				path.push_back(p);
				p = pre[p];
			} 
			
			//倒序输出路径
			for(int j=path.size()-1;j>=0;j--){
				cout<<path[j]<<" ";
			}
			cout<<endl;
		}
	} 

	return 0;
}
/*
9 16
0 1 1
0 2 5
1 2 3
1 3 7
1 4 5
2 4 1
2 5 7
3 4 2
3 6 3
4 5 3
4 6 6
4 7 9
5 7 5
6 7 2
6 8 7
7 8 4
0

0到0的最短距离为:0,路径是0
0到1的最短距离为:1,路径是0 1
0到2的最短距离为:4,路径是0 1 2
0到3的最短距离为:7,路径是0 1 2 4 3
0到4的最短距离为:5,路径是0 1 2 4
0到5的最短距离为:8,路径是0 1 2 4 5
0到6的最短距离为:10,路径是0 1 2 4 3 6
0到7的最短距离为:12,路径是0 1 2 4 3 6 7
0到8的最短距离为:16,路径是0 1 2 4 3 6 7 8
*/

时间复杂度为:O(n^2)


写在末尾

以上就是我对Dijkstra算法的理解了,本人水平有限,文字里没有体现的东西也尽力在代码中展示了,若还有细节不周到之处还请多多体谅,希望对大家有所帮助!

相关推荐
沉鱼.442 小时前
树形DP题目
算法·深度优先
VelinX2 小时前
【个人学习||算法】多维动态规划
学习·算法·动态规划
AlenTech2 小时前
139. 单词拆分 - 力扣(LeetCode)
算法·leetcode·职场和发展
墨韵流芳2 小时前
CCF-CSP第41次认证第一题——平衡数
c++·算法·ccf·平衡数
Book思议-3 小时前
【数据结构实战】栈的经典应用:后缀表达式求值 +中缀转后缀 ,原理 + 代码双通透
数据结构·算法··后缀表达式·后缀转中缀
炽烈小老头3 小时前
【 每天学习一点算法 2026/03/30】跳跃游戏
学习·算法
m0_626535203 小时前
今日需要注意
数据结构
wuweijianlove3 小时前
算法性能预测的统计模型与参数敏感性分析的技术6
算法
Just right3 小时前
重学算法 数组 LC27移除元素
数据结构·算法