当前位置: 首页 > article >正文

再述 Dijkstra

再述 Dijkstra

学 Dijkstra 好久了,今天再学了一遍,感觉推翻了好多自己的知识……

定义

一种用于求非负权值的图的单源最短路径的算法。

方法

已知:如果要求从起始点 s 到某一个点 x 的最短路径,显然只能从某一个已确认为最短路径的点转移。

给个图:

假设我们的起始点是点 1,现在我们用数组记录从原点到所有点的最短路径:

12345
0 ∞ \infty ∞ \infty ∞ \infty ∞ \infty

由于其他点的最短路未知,故先用 ∞ \infty 代替,代码中用很大的一个数字代替即可。

注意到,我们由于要求出某个点出发,所有点的最短路,显然需要更新 n n n 次,其中 n n n 为顶点数量。

在这 n n n 次循环中,我们可以处理出由若干顶点组成的已知最短路集合,称之为 K K K

在每次循环中,用 O ( n ) O(n) O(n) 可以找到距离 u ( u ∈ K ) u(u\in K) u(uK) 最近的那个点,更新其最短路表格,并将其加入 K K K​ 中。

最后得到的结果:

12345
05676

证明

如何证明这种算法是对的?

假设我们有一张图:

a a a 出发,求到 e e e 的最短路径。其中 a → b → e a\rightarrow b\rightarrow e abe 这条路径已确认最短。

显然 a → c → d a\rightarrow c \rightarrow d acd 这条路径并不会比 a → b → e a\rightarrow b\rightarrow e abe 更优,且 d → e d\rightarrow e de 这条边的权值一定非负(前提),所以 a → b → e a\rightarrow b \rightarrow e abe 这条路径一定是最优的。

算算时间复杂度,两层 O ( n ) O(n) O(n) 的循环,就 O ( n 2 ) O(n^2) O(n2),对于小数据可过。可允许大小约在 n ≤ 1 0 4 n\le 10^4 n104

优先队列优化

想想能否优化时间复杂度?

注意到,由于是要求 n n n 个点的最短路,那么第一层的循环显然不能舍弃。

考虑优化时找到最近点的枚举步骤。

可以用一个优先队列存起来。存的东西:pair 类型,第一个元素是目前的最短路距离,第二个是点的编号。

那么众所周知,优先队列查找一个元素的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn) 的,其中 n n n 为元素个数。

每次查找都是一个 O ( log ⁡ n ) O(\log n) O(logn) n n n 次外循环,每次还要通过 O ( m ) O(m) O(m) 的时间复杂度更新最短距离。

所以时间复杂度即为 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn)

一般来说,只要图是联通的, m m m 基本都会比 n n n 大,可近似为 O ( m log ⁡ n ) O (m\log n) O(mlogn)​。

局限性

但是,考虑到一种特殊的情况:完全图。

众所周知,完全图是一种 m = n ( n − 1 ) m=n(n-1) m=n(n1) 的特殊图,那么优先队列优化的时间复杂度就反而退化成了 O ( n 2 log ⁡ n ) O(n^2 \log n) O(n2logn),反而不如朴素版。

代码

放下优先队列优化后的代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXM=5e5+5;
const int MAXN=1e4+5;
int n,m,s;
bool book[MAXN];
int dis[MAXN];
struct EDGE{
	int to,w,pre;
}edge[MAXM];
int head[MAXN];
priority_queue<pair<int,int>,vector<pair<int,int> > ,greater<pair<int,int> > > heap;
void init()
{
	for(int i=1;i<=n;i++)
	{
		dis[i]=INT_MAX;
	}
	return;
}
void add(int from,int to,int w,int num)
{
	edge[num].to=to;
	edge[num].w=w;
	edge[num].pre=head[from];
	head[from]=num;
	return;
}
int u,v,w;
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	dis[s]=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w,i);
	}
	heap.push(make_pair(0,s));
	while(!heap.empty())
	{
		int t=heap.top().second;
		heap.pop();
		if(book[t]==true)
		{
			continue;
		}
		book[t]=true;
		for(int i=head[t];i!=0;i=edge[i].pre)
		{
			dis[edge[i].to]=min(dis[edge[i].to],dis[t]+edge[i].w);
			heap.push(make_pair(dis[edge[i].to],edge[i].to));
		}
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d ",dis[i]);
	}
	puts("");
	return 0;
}

http://www.kler.cn/a/519015.html

相关文章:

  • Qt监控系统辅屏预览/可以同时打开4个屏幕预览/支持5x64通道预览/onvif和rtsp接入/性能好
  • 使用云服务器自建Zotero同步的WebDAV服务教程
  • SpringBoot基础概念介绍-数据源与数据库连接池
  • 使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化
  • 奖励模型:解析大语言模型的关键工具
  • An OpenGL Toolbox
  • 【Elasticsearch】聚合分析:度量聚合
  • 互动视频还是游戏?还是?世界模型
  • nginx部署前端项目
  • docker-compose篇---创建jupyter并可用sudo的创建方式
  • MySQL 基础学习(2): INSERT 操作
  • CLion入门2.0(优雅进行STM32和ESP32开发)(船新版本)
  • 【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文
  • 将 OneLake 数据索引到 Elasticsearch - 第二部分
  • NX100 参数配置
  • 图片导入到ppt之后再打印就糊掉了如何解决?
  • AUTOSAR从入门到精通-【AUTOSAR】OS模块中的Alarm详解
  • spring cloud如何实现负载均衡
  • 【Paper Tips】随记2-word版快速删除某字符
  • Flutter:自定义Tab切换,订单列表页tab,tab吸顶
  • [STM32 标准库]定时器输出PWM配置流程 PWM模式解析
  • C++ 入门速通-第2章【黑马】
  • ASP.NET Core MVC
  • Kafka常见问题之Kafka 报错:org.apache.kafka.common.errors.NotLeaderOrFollowerException
  • 蓝桥杯例题二
  • ‌春节旅游高峰,人力资源如何巧妙应对?