Dijkstra 最短路径算法 秒懂详解
想必大家一定会Floyd了吧,Floyd只要暴力的三个for就可以出来,代码好背,也好理解,但缺点就是时间复杂度高是O(n³)。
于是今天就给大家带来一种时间复杂度是O(n²),的算法:Dijkstra(迪杰斯特拉)。
这个算法所求的是单源最短路,好比说你写好了Dijkstra的函数,那么只要输入点a的编号,就可算出图上每个点到这个点的距离。
我先上一组数据(这是无向图):
图大概是这个样子:
Dijkstra 算法是一种类似于贪心的算法,步骤如下:
1、当到一个时间点时,图上部分的点的最短距离已确定,部分点的最短距离未确定。
2、选一个所有未确定点中离源点最近的点,把他认为成最短距离。
3、再把这个点所有出边遍历一边,更新所有的点。
下面模拟一下:
我们以1为源点,来求所有点到一号点的最短路径。
先建立一个dis数组,dis[i]表示第i号点到源点(1号点)的估计值,你可能会问为什么是估计值,因为这个估计值会不断更新,更新到一定次数就变成答案了,这个我们一会再说。
然后我们在建立一个临界矩阵,叫做:map,map[i][j]=v表示从i到j这条边的权值是v。
dis初始值除了源点本身都是无穷大。源点本身都是0.
先从1号点开始。一号点,map[1][2]=5,一号点离2号点是5,比无穷大要小,所以dis[2]从无穷大变成了5。顺便,我们用minn记录距离1号点最短的点,留着以后会用。
dis[0,5,∞,∞,∞]。minn=2。
然后搜到3号点,map[1][3]=8,距离是8,比原来的dis[3]的∞小,于是dis[3]=8。但是8比dis[2]的5要大,所以minn不更新。
dis[0,5,8,∞,∞]
接着分别搜索4,5号点,发现map[1][4],map[1][5]都是∞,所以就不更新。
现在,dis数组所呈现的明显不是最终答案,因为我们才更新一遍,现在我们开始第二次更新,第二次更新以什么为开始呢?就是以上一次我们存下来的,minn,相当于把2当源点,求所有点到它的最短路,加上它到真正的源点(1号点)的距离,就是我们要求的最短路。
从2号点开始,搜索3号点,map[2][3]=1,原本dis[3]=8,发现dis[2]+map[2][3]=5+1=6<dis[3](8)所以更新dis[3]为6,minn=3
dis[0,5,6,∞,∞] minn=3.
然后搜索4号点,map[2][4]=3,原本dis[4]=∞,所以,dis[2]+map[2][4]=5+3=8<dis[4](∞)所以更新dis[4]=8,因为map[2][4]=3,3>1,minn不更新。
dis[0,5,6,8,∞] minn=3.
接着搜索5号点,map[2][5]=2,5+2=7,7<∞,dis[5]=7minn不变。
dis[0,5,6,8,7]
二号点搜完,因为minn是3,继续搜索3号点。
三号点还是按照二号点的方法搜索,发现没有可以更新的,然后搜索四号。
四号搜5号点,发现8+7>5+2,所以依然不更新,然后跳出循环。
现在的估计值就全部为确定值了:
dis[0,5,6,8,7]
这就是每个点到源点一号点的距离,我们来看一下代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int map[][];//这就是map数组,存储图
int dis[];//dis数组,存储估计值
int book[];//book[i]代表这个点有没有被当做源点去搜索过,1为有,0为没有。这样就不会重复搜索了。
int n,m;
void dijkstra(int u)//主函数,参数是源点编号
{
memset(dis,,sizeof(dis));//把dis数组附最大值(88不是十进制的88,其实很大)
int start=u;//先从源点搜索
book[start]=;//标记源点已经搜索过
for(int i=;i<=n;i++)
{
dis[i]=min(dis[i],map[start][i]);//先更新一遍
}
for(int i=;i<=n-;i++)
{
int minn=;//谢评论区,改正一下:这里的minn不是题解上的minn,这代表的是最近点到源点的距离,start才代表最近的点、
for(int j=;j<=n;j++)
if(book[j]== && minn>dis[j])
{
minn=dis[j];
start=j;//找到离源点最近的点,然后把编号记录下来,用于搜索。
}
book[start]=;
for(int j=;j<=n;j++)
dis[j]=min(dis[j],dis[start]+map[start][j]);//以新的点来更新dis。
}
}
int main()
{
cin>>n>>m;
memset(map,,sizeof(map));
for(int i=;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
map[a][b]=c;
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
if(i==j)
map[i][j]=;
dijkstra();//以1为源点。
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
}
这就是用邻接矩阵实现dijkstra,但是这个算法有一个坏处,就是出现负权边,这个算法就炸了,要解决负权边,我以后会给大家带来Bell man ford(SPFA)
这个算法的复杂度是O(n²),空间复杂度也是n平方,如果用邻接表来实现,最差情况,时间复杂度是O(n*m)似乎比n²要大一些,但是空间复杂度会从n平方变成m,少了很多,现在我呈上邻接表的代码。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int value[],to[],next[];
int head[],total;
int book[];
int dis[];
int n,m;
void adl(int a,int b,int c)
{
total++;
to[total]=b;
value[total]=c;
next[total]=head[a];
head[a]=total;
}
void dijkstra(int u)
{
memset(dis,,sizeof(dis));
memset(book,,sizeof(book));
dis[u]=;
for(int i=;i<=n;i++)
{
int start=-;
for(int j=;j<=n;j++)
if(book[j]== && (dis[start]>dis[j] || start==-))
start=j;
book[start]=;
for(int e=head[start];e;e=next[e])
dis[to[e]]=min(dis[to[e]],dis[start]+value[e]);
}
}
int main()
{
cin>>n>>m;
for(int i=;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
adl(a,b,c);
}
dijkstra();
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
}
一年多了,身为一个OIer,经历了太多。
当年那么畏惧的Dijkstra、邻接表,现在已经是信手拈来。
那个暑假,因为Djkstra名字的朗朗上口,讲自己名字改为了Dijkstra,但是逐渐因为SPFA的可处理负权边,也将Dijkstra,淡忘。
如今突然想起,加入了堆优化,有人说:一道题如果边权没有负数,那么一定是在卡SPFA。这时候就用到了堆优化的Dijkstra。
一年前提到,朴素的Dijkstra时间复杂度是n^2,被SPFA的m*常数吊打,但是,经过堆优化,Dijkstra的时间复杂度能达到nlogn,如果这个图特别稠密的话,也就是m特别大(比如完全图就是n^2),那么nlogn是要小于m的,这就用到了Dijkstra
首先堆优化怎么优化?观察上面的代码,每次循环中都再嵌套一个循环求dis值最小的点。这里,我们可以用一个优先队列,每当搜索到一个新点,扔到优先队列里面,这样每次就取队首的绝对是最优值。这样可以省去for循环。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define in(a) a=read()
#define REP(i,k,n) for(long long i=k;i<=n;i++)
#define MAXN 10010
using namespace std;
typedef pair<long long,long long> P;
inline long long read(){
long long x=,t=,c;
while(!isdigit(c=getchar())) if(c=='-') t=-;
while(isdigit(c)) x=x*+c-'',c=getchar();
return x*t;
}
long long n,m,s;
long long total=,head[MAXN],nxt[MAXN<<],to[MAXN<<],val[MAXN<<];
long long dis[MAXN],vis[MAXN];
priority_queue <P, vector<P>,greater<P> > Q;//优先队列优化
inline void adl(long long a,long long b,long long c){
total++;
to[total]=b;
val[total]=c;
nxt[total]=head[a];
head[a]=total;
return ;
}
inline void Dijkstra(){
REP(i,,n) dis[i]=;
dis[s]=;
Q.push(P(,s));
while(!Q.empty()){
long long u=Q.top().second;//取出dis最小的点
Q.pop();//弹出
if(vis[u]) continue;
vis[u]=;
for(long long e=head[u];e;e=nxt[e])
if(dis[to[e]]>dis[u]+val[e]){
dis[to[e]]=dis[u]+val[e];
Q.push(P(dis[to[e]],to[e]));//插入
}
}
return ;
}
int main(){
in(n),in(m),in(s);
long long a,b,c;
REP(i,,m) in(a),in(b),in(c),adl(a,b,c);
Dijkstra();
REP(i,,n) printf("%lld ",dis[i]);
}
Dijkstra 最短路径算法 秒懂详解的更多相关文章
- CRF(条件随机场)与Viterbi(维特比)算法原理详解
摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...
- 一致性算法RAFT详解
原帖地址:http://www.solinx.co/archives/415?utm_source=tuicool&utm_medium=referral一致性算法Raft详解背景 熟悉或了解 ...
- 各大公司广泛使用的在线学习算法FTRL详解
各大公司广泛使用的在线学习算法FTRL详解 现在做在线学习和CTR常常会用到逻辑回归( Logistic Regression),而传统的批量(batch)算法无法有效地处理超大规模的数据集和在线数据 ...
- 转】Mahout推荐算法API详解
原博文出自于: http://blog.fens.me/mahout-recommendation-api/ 感谢! Posted: Oct 21, 2013 Tags: itemCFknnMahou ...
- MD5算法步骤详解
转自MD5算法步骤详解 之前要写一个MD5程序,但是从网络上看到的资料基本上一样,只是讲了一个大概.经过我自己的实践,我决定写一个心得,给需要实现MD5,但又不要求很高深的编程知识的童鞋参考.不多说了 ...
- [转]Mahout推荐算法API详解
Mahout推荐算法API详解 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeepe ...
- Java邻接表表示加权有向图,附dijkstra最短路径算法
从A到B,有多条路线,要找出最短路线,应该用哪种数据结构来存储这些数据. 这不是显然的考查图论的相关知识了么, 1.图的两种表示方式: 邻接矩阵:二维数组搞定. 邻接表:Map<Vertext, ...
- 2. EM算法-原理详解
1. EM算法-数学基础 2. EM算法-原理详解 3. EM算法-高斯混合模型GMM 4. EM算法-高斯混合模型GMM详细代码实现 5. EM算法-高斯混合模型GMM+Lasso 1. 前言 概率 ...
- 练习 Dijkstra 最短路径算法。
练习 Dijkstra 最短路径算法. #coding: utf-8 # Author: woodfox, Oct 14, 2014 # http://en.wikipedia.org/wiki/Di ...
随机推荐
- Java面试题整理1
Java基础部分 JDK和JRE有什么区别? JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境.JRE:Java Runtime ...
- Hadoop系列-HDFS基础
基本原理 HDFS(Hadoop Distributed File System)是Hadoop的一个基础的分布式文件系统,这个分布式的概念主要体现在两个地方: 数据分块存储在多台主机 数据块采取冗余 ...
- VS2015创建WDK的问题
在微软官网找了半天.. 搜索window driver kit,好吧.进入一页英文页面.. https://docs.microsoft.com/en-us/windows-hardware/driv ...
- CAN总线的学习
CAN(controller Area Network )总线的学习 1986 年德国电气商博世公司开发出面向汽车的CAN 通信协议(数据之间的交互),其目的是为适应“减少线束的数量”.“通过多个LA ...
- 生成并调用so动态库
本文更新于2019-01-03. 生成库 头文件fn.h如下: #ifndef __FN_H__ #define __FN_H__ #ifdef __cplusplus extern "C& ...
- 5.18-笨办法学python-习题17(文件拷贝)
from sys import argv from os.path import exists #又import了一个命令exists,这个命令将文件名字符串作为参数,如果文件存在返回TRUE,否则返 ...
- 基于 OpenResty 实现一个 WS 聊天室
基于 OpenResty 实现一个 WS 聊天室 WebSocket WebSocket 协议分析 WebSocket 协议解决了浏览器和服务器之间的全双工通信问题.在WebSocket出现之前,浏览 ...
- 一些有趣的 Shell 命令
find . -name "*.db" -type f 查找当前路径下名称满足正则*.db的文件,-type d 则是查找文件夹 grep -rn "Main" ...
- 20155207 《Java程序设计》实验报告二:Java面向对象程序设计
实验要求 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O.L.I.D原则 5.了解设计模式 实验内容 一.单元测试 1.三种代码 ...
- 20155211 2016-2017-2 《Java程序设计》第五周学习总结
20155211 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 这周学习了第八章和第九章的内容.第八章和第九章主要就是介绍一些类的应用,我先把教材上的内容看 ...