最短路算法之 Dijkstra
部分内容参考了李煜东的《算法竞赛进阶指南》,在此声明。
单源最短路径
单源最短路径问题,是说,给定一张有向图(无向图)\(G=(V,E)\) ,\(V\) 是点集,\(E\) 是边集,\(|V|=n\),\(|E|=m\),节点是 \([1,n]\) 之间的连续整数,\((x,y,z)\) 描述一条从 \(x\) 到 \(y\) 边长为 \(z\) 的有向(无向)边,设 1 号点为起点,求长度为 \(n\) 的数组 \(dist\),其中 \(dist_i\) 表示从1到 \(i\) 的最短路径的长度。
Dijkstra 算法
Dijkstra 算法可以求解不含有负边权单源最短路问题,其本质是贪心。
下面来介绍它的算法实现流程。Dijkstra 算法将图中所有点分为2大类,下面不妨设为集合 \(E1\) 与集合 \(E2\)。其中 \(E1\) 表示的是已经确定最短路径的点,\(E2\) 表示的是还未确定最短路径的点。
Dijkstra 的算法流程如下:- 初始化 \(dist_1=0\),因为1号点是起点。
- 找出一个点 \(x \in E2\) 且 \(dist_x\) 最小,并将 \(x\) 加入集合 \(E1\)。
- 枚举 \(x\) 的所有出边 \((x,y,z)\) 更新 \(dist_y\) 的值,具体的,若
dist[y] > dist[x] + z则dist[y] = dist[x] + z。 - 重复执行2,3操作直至所有点均被加入 \(E1\) 集合。
这里可能会有人产生疑惑,为什么我们一旦找出 \(x\) 就可以把它加入 \(E1\) 呢,这里就体现出了 Dijkstra 算法的核心与本质,贪心就用在这里。\(x\) 是 \(E2\) 中的点,也就是说 \(dist_x\) 还不确定,看起来好像不能将 \(x\) 加入 \(E1\) 集合,但仔细思考发现,能更新 \(dist_x\) 的点一定不在 \(E1\) 里,因为一旦某个点被加入了 \(E1\) ,那么在这之前,这个点一定会先更新它所能更新的点,所以能更新 \(dist_x\) 的点一定在 \(E2\) 中。其次,\(dist_x\) 是 \(E2\) 中最小的,又因为边权非负,所以 \(E2\) 中的点也无法更新 \(dist_x\) ,综上,\(dist_x\) 无法被任何一个点更新,所以 \(dist_x\) 不会更小,\(x\) 就可以顺利地加入集合 \(E1\) 了。
下面我们分析一下这个算法的复杂度。
根据上面的算法流程可知,在每一轮的循环中,必会有一个点被加入 \(E1\) 中(操作1),而循环终止条件是所有点均被加入集合 \(E1\),所以,我们最多循环 \(n\) 次。而每一次操作2的复杂度是 \(O(n)\),所以总复杂度就是 \(O(n^2)\)。\(O(n^2)\) 部分的代码,对应这道模板题:
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 510,inf = 0x7f7f7f7f;
int n,m;
int g[maxn][maxn],dis[maxn];
bool vis[maxn];
int dijkstra(){
memset(dis,inf,sizeof(dis));
dis[1] = 0;
for(int i = 1;i <= n;i++){
int mn = 0;
for(int j = 1;j <= n;j++)
if(!vis[j] && (!mn || dis[j] < dis[mn])) mn = j;
vis[mn] = 1;
for(int j = 1;j <= n;j++)
if(!vis[j] && g[mn][j] != inf) dis[j] = min(dis[j],dis[mn] + g[mn][j]);
}
return dis[n] == inf ? -1 : dis[n];
}
int main(){
scanf("%d%d",&n,&m);
memset(g,inf,sizeof(g));
int u,v,w;
while(m--){
scanf("%d%d%d",&u,&v,&w);
g[u][v] = min(g[u][v],w);
}
printf("%d",dijkstra());
return 0;
} 作者:旭日临窗
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
但是在有些题中,点数和边数非常大,通常 \(n \le 10^5 , m \le 10^5\)。我们 \(O(n^2)\) 的代码无法通过,所以下面我们考虑优化。
我们观察 \(O(n^2)\) 的代码,发现复杂度瓶颈在于内层循环,也就是操作2,所以我们重点考虑怎么优化操作2,也就是怎么快速找出 \(E2\) 中最小的 \(dist_x\)。在一个集合中找最小值,你想到了什么数据结构?没错,就是优先队列,或者说小堆根。具体的,每次当执行到操作3,并且 \(dist_y\) 可以被更新的时候,就将 \((y , dist_y)\) 这个二元组,加入优先队列,注意,优先队列默认是大堆根,所以我们可以用
pair <int,int>来储存二元组,并且利用pair的内置比较函数,可以很方便的重载优先队列,将大堆根变成小堆根。这样操作2就变成了在优先队列的队头取出 \(x\) 和 \(dist_x\) 。复杂度可以降至 \(O(m \log n)\)。\(O(m \log n)\) 的代码,对应这道题
点击查看代码
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define PII pair <int,int>
using namespace std;
const int maxn = 2e5 + 10,inf = 0x7f7f7f7f;
int n,m,cnt;
int head[maxn],dis[maxn];
bool vis[maxn];
struct edge{int to,nxt,w;}e[maxn];
void add(int u,int v,int w){e[cnt] = edge{v,head[u],w};head[u] = cnt++;}
priority_queue <PII,vector <PII>,greater <PII> > q;
int dijkstra(){
memset(dis,0x7f,sizeof(dis));
dis[1] = 0;
q.push({0,1});
while(!q.empty()){
int u = q.top().second;q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u];~i;i = e[i].nxt){
int v = e[i].to;
if(dis[u] + e[i].w < dis[v]){
dis[v] = dis[u] + e[i].w;
q.push({dis[v],v});
}
}
}
return dis[n] == inf ? -1 : dis[n];
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
int u,v,w;
while(m--){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
printf("%d",dijkstra());
return 0;
} 作者:旭日临窗
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最短路算法之 Dijkstra的更多相关文章
- 【最短路算法】Dijkstra+heap和SPFA的区别
单源最短路问题(SSSP)常用的算法有Dijkstra,Bellman-Ford,这两个算法进行优化,就有了Dijkstra+heap.SPFA(Shortest Path Faster Algori ...
- 最短路算法之 Dijkstra算法
Dijkstra算法 Dijkstra算法是典型最短路算法,用于计算一个节点到其它全部节点的最短路径. 主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法能得出最短路径的最 ...
- 最短路算法之Dijkstra算法通俗解释
Dijkstra算法 说明:求解从起点到任意点的最短距离,注意该算法应用于没有负边的图. 来,看图. 用邻接矩阵表示 int[][] m = { {0, 0, 0, 0, 0, 0}, {0, 0, ...
- 【最短路算法】Dijkstra知识点&代码
代码: #include<iostream> #include<vector> #include<cstdio> #include<queue> #in ...
- Book 最短路算法
用HDU2544整理一下最近学的最短路算法 1.Dijkstra算法 原理:集合S表示已经找到最短路径的点,d[]表示当前各点到源点的距离 初始时,集合里面只有源点,当每个点u进入集合S时,用d[u] ...
- Dijkstra 最短路算法(只能计算出一条最短路径,所有路径用dfs)
上周我们介绍了神奇的只有五行的 Floyd 最短路算法,它可以方便的求得任意两点的最短路径,这称为"多源最短路".本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做&q ...
- Dijkstra最短路算法
Dijkstra最短路算法 --转自啊哈磊[坐在马桶上看算法]算法7:Dijkstra最短路算法 上节我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最 ...
- 【啊哈!算法】算法7:Dijkstra最短路算法
上周我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最短路”.本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径”.例如求下图 ...
- 【坐在马桶上看算法】算法7:Dijkstra最短路算法
上周我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最短路”.本周来来介绍指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径 ...
- (转)最短路算法--Dijkstra算法
转自:http://blog.51cto.com/ahalei/1387799 上周我们介绍了神奇的只有五行的Floyd最短路算法,它可以方便的求得任意两点的最短路径,这称为“多源最短 ...
随机推荐
- 微信小程序云开发使用Typescript
1.首先参考陈希章@中国在微信小程序开发中使用Typescript中的内容了解相关的内容 2.按以下步聚进行设置 1)确认本机环境已安装npm.通过在安装目录下输入 npm --v 如返回具体的版本, ...
- 大小写字符转换【Sql Server和C#两种写法】
案例:Var Str = "abdCnd" 如何将Str = "ABDCND"? Sql Server写法:upper(Str) ==> Lower ...
- 虚拟机中debian11修改控制台(console)分辨率|linux控制台分辨率|linux console resolution
实体机一般安装好驱动分辨率就没啥问题,而且个人pc也没有只用控制台的需求.但是虚拟机中不安装桌面的时候,默认的控制台分辨率常不能满足需求. 这个需求貌似也比较少,而且几乎搜到的文章大部分都是旧的,不能 ...
- nignx 代理前端服务
说明:研发给了一个前端包,需要进行代理访问 前端包名:web 一.部署nginx服务 略 二.配置代理 2.1 将前端包放置任意目录,如/home/manage 2.2 修改nginx配置 [root ...
- 微信小程序搜索排名权重!
最后,再介绍一下排名权重的计算比例: 1.小程序上线时间(占比5%) 2.描述中完全匹配出现关键词次数越多,排名越靠前(10%) 3.标题中关键词出现1次,且整体标题的字数越短,排名越靠前(35%) ...
- 路由器、集线器、交换机分别工作在OSI七层协议模型的哪一层
路由器三层(网络层): 集线器一层(物理层): 普通交换机二层(数据链路层). 现在也有工作在第三层的交换机. OSI七层网络模型由下至上为1至7层,分别为物理层(Physical layer),数据 ...
- 将pb模型参数提取转成torch模型
1 import tensorflow as tf 2 import onnx 3 import onnxsim 4 import numpy as np 5 import torch 6 from ...
- sudo apt update 没有 Release 文件
注: 不同环境出错原因可能不同,本文仅供参考. 今天在Ubuntu 19.04 系统运行指令 sudo apt update 时,忽然提示错误,报错如下: 404 Not Found [IP: 101 ...
- RNN,LSTM,BERT
目录 RNN LSTM 计算公式 参数量计算 self-attention bert 论文 源码 问题 问题:bert中进行ner为什么没有使用crf:使用DL进行序列标注问题的时候CRF是必备嘛(t ...
- 在centons7系统部署一套单master的k8s集群
架构图: 操作系统:CentOS Linux release 7.7.1908 (Core) docker:docker-ce-20.10.14-3.el7.x86_64 kubernetes: 1. ...