Dijkstra——单源最短路径

dijkstra------单源最短路径

该算法用来解决带权图的单源最短路径问题,也就是说求起点带每个点的最短路程,这是一个贪心算法,同样这给我们这些小菜鸡带来了巨大震撼,太难了!!!要知后事如何,且听我徐徐道来~

我们想要算一个起点到别的所有点的最短路径,我们应该怎样来实现呢?首先想到暴力,但是一旦顶点多一点,路径的数量直接爆炸。然后我们就想Floyd可不可以,但是我们不要忘了Floyd适合求每一对点的最短路,而你只需要从1开始,这样未免太浪费了吧。SPFA呢?它是可以处理负边,但是速度要比Dijkstra慢许多。这时我们就要用Dijkstra啦~

我们首先挑一个没被标记的且目前距离最小的u,这个是我们从起点到u的最短距离,然后标记以便以后不再被更新,接下来就开始独属于我们Dijkstra的松弛了。用u来更新他的相邻点的距离,更新从起点到所在的点的最小值。

第一次 第二次 第三次 第四次 第五次
b 2 (a,b)
c 5(a,c) 3 (a,b,c)
d ∞\infty∞ 5(a,b,d) 5(a,b,d) 5 (a,b,d)
e ∞\infty∞ ∞\infty∞ 7(a,b,c,e) 7(a,b,c,e) 6 (a,b,c,e)
f ∞\infty∞ ∞\infty∞ 4 (a,b,c,f)
终点集 {a,b} {a,b,c} {a,b,c,f} {a,b,c,f,d} {a,b,c,f,d,e}

那么接下来我们就来实现一下代码:

例子

3 3

1 2 2

1 3 5

2 3 1

0 2 3

7 10

1 2 5

1 3 2

2 4 1

3 4 6

3 6 8

2 5 6

4 6 2

4 5 1

5 7 7

6 7 3

0 5 2 6 7 8 11

朴素版:

复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1005,INF=0x3f3f3f3f;
int g[N][N];//邻接矩阵,代表u直达v的边权
int dis[N];//起点到x当前最短距离
bool vis[N];//这个点有没有被标记
void dijkstra(int n,int s)
{
	memset(dis,0x3f,sizeof(dis));//初始化为无穷大
	memset(vis,false,sizeof(vis));//初始化为false
	dis[s]=0;//起点到自身为0
	for(int i=0;i<n;i++)//遍历每个数,每遍历一个确定一个点的最短路径
	{
		int u=-1,min_d=INF;//初始化
		for(int j=1;j<=n;j++)//遍历全部顶点,选一个还没确定最短路,距离值最小的顶点u
		{
			if(!vis[j]&&dis[j]<min_d)//若没有标记并且小于当前最小,更新
			{
				min_d=dis[j];
				u=j;
			}
		}
		if(u==-1) break;//若没有循环结束
		vis[u]=true;//标记
		//松弛操作
		for(int v=1;v<=n;v++)//从起点到u再到v的路径,对比v当前路径,更新最小值
		{
			if(!vis[v]&&g[u][v]!=INF)
			dis[v]=min(dis[v],dis[u]+g[u][v]);
		}
	}
}
signed main()
{
	int n,m;
	cin>>n>>m;
	memset(g,0x3f,sizeof(g));//初始值为无穷大
	for(int i=1;i<=n;i++) g[i][i]=0;//每个点到自身距离都为0
	for(int i=0;i<m;i++)
	{
		int a,b,w;
		cin>>a>>b>>w;
		g[a][b]=min(g[a][b],w);
		g[b][a]=min(g[b][a],w);//无向边
	}
	dijkstra(n,1);//从1开始
	for(int i=1;i<=n;i++)
	{
		if(dis[i]==INF) cout<<"-1"<<" ";//若走不到输出-1
		else cout<<dis[i]<<" ";//若能走到,输出最短路径
	}
	cout<<endl;
	return 0;
}

相信聪明的你已经看出来了,这是朴素版的。那有没有更高级的呢?当然有啦~~ 接下来我们就来讲讲优化的版本------堆优化。

之前那个我们要每一轮遍历去找最小距离的点,而现在我们只用优先队列直接取出距离最小的点,那么复杂度会大大降低哦~

之前那个理解了,这个自然也就好理解多了。我们用小根堆来选出最小路径,再在此基础上进行松弛处理

复制代码
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=101,INF=0x3f3f3f3f;
vector<pii> adj[N];
int dis[N];
bool vis[N];
void dijkstra(int n,int s)
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,false,sizeof(vis));
	dis[s]=0;
	priority_queue<pii,vector<pii>,greater<pii>> q;//小根堆
	q.push({0,s});//原点存入
	while(!q.empty())//若不为空
	{
		auto ars=q.top();
		q.pop();//最小值出栈
		int d=ars.first;
		int u=ars.second;
		if(vis[u]) continue;//若被标记,则说明有最小值,这个值可以舍去了continue
		vis[u]=true;//未被标记,说明当前是最短距离,标记
		for(auto ans:adj[u])//我们来松弛一下相邻点
		{
			int v=ans.first;
			int w=ans.second;
			if(dis[v]>dis[u]+w)//若之前的最短距离>当前的,更新值
			dis[v]=dis[u]+w;
			q.push({dis[v],v});//入栈,这里旧的长的不用出栈,下一个循环if(vis[u]) continue;会判断
		}
	}
}
signed main()
{
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int a,b,w;
		cin>>a>>b>>w;
		adj[a].emplace_back(b,w);//这里用g[][]容易超限,所以用vector来存
		adj[b].emplace_back(a,w);
	}
	dijkstra(n,1);
	for(int i=1;i<=n;i++)
	{
		if(dis[i]==INF) cout<<"-1"<<" ";
		else cout<<dis[i]<<" ";
	}
	return 0;
}

好了,这就是dijkstra的基础原理及代码,希望对你有所帮助>^<~~

相关推荐
weixin_3077791317 分钟前
AI生成代码的隐患与治理:人工审查流程及提示词驱动的修复策略
开发语言·人工智能·算法
zmzb010325 分钟前
C++课后习题训练记录Day201
c++·算法·图论
lch2011_yb28 分钟前
CSP-J 2023 小苹果
算法
星星.72239 分钟前
【图论】最小生成树|Prim+Kruskal算法
数据结构·c++·算法·图论
_Narcissus_1 小时前
单调栈笔记及例题详解
数据结构·c++·笔记·算法·力扣·单调栈·洛谷
土司大王9 小时前
LeetCode hot100——对称二叉树
算法·leetcode·职场和发展
sali-tec9 小时前
C# 基于OpenCv的视觉工作流-章106-差值追踪
图像处理·人工智能·opencv·算法·计算机视觉
Navigator_Z9 小时前
LeetCode //C - 1208. Get Equal Substrings Within Budget
c语言·算法·leetcode