一般最短路径算法习惯性的分为两种:单源最短路径算法和全顶点之间最短路径。前者是计算出从一个点出发,到达所有其余可到达顶点的距离。后者是计算出图中所有点之间的路径距离。


单源最短路径

Dijkstra算法

思维

本质上是贪心的思想,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:S,原本的元素构成集合Q,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合S只有顶点s

然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到S中,此时完成一个顶点, 然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。 然后,又从dis中找出最小值,重复上述动作,直到S中包含了图的所有顶点。

但是很可惜,由于算法特性,一个点的距离确定后不会再改变,这里的确定不是指得到数值,而且该点被作为观察点观察后所以这个算法并不能处理带负权边的图。

但是可以利用该算法+优先队列来优化,优化后的算法打破了之前的一个点的距离确定后不会再改变的算法特性,更加类似SPFA,并且可以处理负权边。但个人觉得已经不能称作dijkstra算法了。


证明

这里给出一个命题:从集合Q中找到dis[k]最小的v,dis[k]即为源点到v的最短路径长度。

如果这个命题为真,dij的正确性就可以得证。

  • 证明:从开始利用算法取得一个v1,即dis[v1]是最小的,\(\forall\)v,dis[v]>dis[v1]。

    假设dis[v1]不是从源点到v1最短路径

    则\(\exists\)v,使得dis[v]<dis[v1],与已知矛盾。

    得证。
  • 证明:已利用算法从Q中找到k个点,并确定了k个点的最短路径,此时再从Q中用算法找出一个vk+1,dis[vk+1]即为源点到vk+1最短路径。

    假设dis[vk+1不是源点到vk+1的最短路径长度。

    则设从源点到vk+1的最短路径经过的点的集合为V,dis为路径长度,切dis < dis[vk+1]。

    设V中最靠近vk+1且不属于S的点为vx,vx的后继点为vy

    。如果有向图中皆为正权边,则易得dis[vx] < dis[vy] <= dis。(vy = vk+1时等号成立)

    但又因为vx不属于S,则dis[vx] > dis,矛盾。

    得证。
  • 综上所述,命题得证。
  • 负权边的时候,dis[vx] < dis[vy]和dis[vx] > dis都不一定成立。故不得证。

举例演算

集合S 当前观察点u dis[2] dis[3] dis[4] dis[5] dis[6]
1 - 1 2
1,2 2 1 2 4 6
1,2,4 4 1 5 2 4 6
1,2,4,5 5 1 5 2 4 6
1,2,4,5,3 3 1 5 2 4 5
1,2,4,5,3,6 6 1 5 2 4 5

从结点1出发,1与2、4连通,确定(1,2),(1,4)的距离,其中到2的距离最短,再从观察点2出发,2与5、6连通,根据dis[u]+c[u][v]<dis[v]的判断关系出发,更新dis。接着再分别从观察点4,5,3,6出发更新dis,得到最终的结点1的单源最短路径。


代码实现

朴素dij算法,时间复杂度约为O(n2)

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue> using namespace std; const int maxn = 1000;
const int inf = 0x7fffffff; int n, m;
int e[maxn][maxn], dis[maxn];
int book[maxn]; void dij(int s) {
for (int i = 1; i <= n; i++) dis[i] = e[s][i];
for (int i = 1; i <= n; i++) book[i] = 0;
book[s] = 1;
dis[s] = 0; for (int i = 1; i <= n - 1; i++) {
int min = inf;
int u;
for (int j = 1; j <= n; j++) { if (book[j] == 0 && dis[j] < min) {
min = dis[j];
u = j;
}
}
book[u] = 1;
for (int v = 1; v <= n; v++) {
if (e[u][v] < inf && book[v] == 0) {
if (dis[v] > dis[u] + e[u][v]) {
dis[v] = dis[u] + e[u][v];
}
}
}
}
} int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
e[i][j] = inf;
}
}
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[u][v] = w;
}
int x;
cin >> x;
dij(x);
for (int i = 1; i <= n; i++) {
cout << dis[i] << " ";
}
return 0;
}

为了能够方便的寻找当前最小的dis作为观察点,可以利用优先队列最小堆来优化。同时利用初始化建表来节省寻找每个点的相邻点的过程。这里使用的是stl中的优先队列,底层是heap实现的,应该是二叉堆,所以时间复杂度应该是O((m+n)logn),如果是使用斐波那契堆,可以到O(nlogn)。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue> using namespace std; const int MAX = 1000; int h[MAX * 2], to[MAX * 2], nxt[MAX * 2], co[MAX * 2], dis[MAX], k = 0, n, m;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que; void insert(int u,int v,int c) {
nxt[++k] = h[u];
h[u] = k;
to[k] = v;
co[k] = c;
nxt[++k] = h[v];
h[v] = k;
to[k] = u;
co[k] = c;
} void dij(int s) {
for (int i = 0; i < MAX; i++) dis[i] = 0x7FFFFFFF;
dis[s] = 0;
que.push(make_pair(dis[s], s));
while (!que.empty()) {
pair<int, int> u = que.top();
que.pop();
if (dis[u.second] < u.first) continue;
for (int i = h[u.second]; i; i = nxt[i]) {
if (dis[to[i]] > dis[u.second] + co[i]) {
dis[to[i]] = dis[u.second] + co[i];
que.push(make_pair(dis[to[i]], to[i]));
}
}
}
} int main() {
cin >> n >> m;
memset(h, 0, sizeof(h));
int u, v, c;
for (int i = 0; i < m; i++) {
cin >> u >> v >> c;
insert(u, v, c);
}
int x;
cin >> x;
dij(x);
for (int i = 1; i <= n; i++) {
if (dis[i] > 100000) cout << "none" << " ";
else cout << dis[i] << " ";
}
cout << endl;
return 0;
}

最短路径——Dijkstra算法以及二叉堆优化(含证明)的更多相关文章

  1. Dijkstra算法的二叉堆优化

    Dijkstra算法的二叉堆优化 算法原理 每次扩展一个距离最小的点,再更新与其相邻的点的距离. 如何寻找距离最小的点 普通的Dijkstra算法的思路是直接For i: 1 to n 优化方案是建一 ...

  2. POJ 3635 - Full Tank? - [最短路变形][手写二叉堆优化Dijkstra][配对堆优化Dijkstra]

    题目链接:http://poj.org/problem?id=3635 题意题解等均参考:POJ 3635 - Full Tank? - [最短路变形][优先队列优化Dijkstra]. 一些口胡: ...

  3. 二叉堆(一)之 图文解析 和 C语言的实现

    概要 本章介绍二叉堆,二叉堆就是通常我们所说的数据结构中"堆"中的一种.和以往一样,本文会先对二叉堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本 ...

  4. 二叉堆(二)之 C++的实现

    概要 上一章介绍了堆和二叉堆的基本概念,并通过C语言实现了二叉堆.本章是二叉堆的C++实现. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的C++实现(完整源码)4. 二叉堆的C++测试程 ...

  5. 二叉堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的Java实现(完整源码) ...

  6. 二叉堆的实现(数组)——c++

    二叉堆的介绍 二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:最大堆和最小堆.最大堆:父结点的键值总是大于或等于任何一个子节点的键值:最小堆:父结点的键值总是小于或等于任何一个 ...

  7. 图论——Dijkstra+prim算法涉及到的优先队列(二叉堆)

    [0]README 0.1)为什么有这篇文章?因为 Dijkstra算法的优先队列实现 涉及到了一种新的数据结构,即优先队列(二叉堆)的操作需要更改以适应这种新的数据结构,我们暂且吧它定义为Dista ...

  8. 《Algorithms算法》笔记:优先队列(2)——二叉堆

    二叉堆 1 二叉堆的定义 堆是一个完全二叉树结构(除了最底下一层,其他层全是完全平衡的),如果每个结点都大于它的两个孩子,那么这个堆是有序的. 二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组 ...

  9. C# 最大二叉堆算法

    C#练习二叉堆算法. namespace 算法 { /// <summary> /// 最大堆 /// </summary> /// <typeparam name=&q ...

随机推荐

  1. c/c++ 表白小程序

    1.开发工具: vs  vc(任选一个) 2.准备材料 : a.一首音乐 (注意:音乐要求重命名为  “x”  ) b.20张图片(注意: 图片要求重命名为  “1”  "2"   ...

  2. CentOS7 64位下 MySQL5.7的安装与配置(YUM)

    趁着11.11的时候在阿里云上弄了一云服务ECS(作为自己的节日礼物 > _ <) ,系统为CentOS的,打算弄一个人博客之类的,这些天正在备案当中(不知得多久). 忙里偷闲,在中午休息 ...

  3. SpringBoot配置全局自定义异常

    不同于传统集中时Springmvc 全局异常,具体查看前面的章节https://www.cnblogs.com/zwdx/p/8963311.html 对于springboot框架来讲,这里我就介绍一 ...

  4. 快速玩转linux(1)

    快速上手Linux玩转典型应用 mark 大牛都会使用Linux, Linux命令是行业要求. 商业服务器基本都是linux 开源软件都先支持Linux(只支持) 大数据分析.机器学习首选Linux ...

  5. ASP.NET安全验证

    一.为什么要用安全验证,使用安全验证有什么好处. 构造特殊的链接地址,导致文件内的数据泄露 数据库泄露 安全防范的首要策略:所有的HTTP访问都要经过IIS,所以限制IIS的安全性是关键 二.安全验证 ...

  6. less学习一

    Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量.Mixin.函数等特性,使 CSS 更易维护和扩展. Less 可以运行在 Node 或浏览器端. less文件只有被编译后才 ...

  7. 【Hive二】 Hive基本使用

    Hive基本使用 创建数据库 创建一个数据库,数据库在HDFS上的默认存储路径是/user/hive/warehouse/*.db create database 库名; 避免要创建的数据库已经存在错 ...

  8. python——元组(tuple)基本操作

    元组被称为只读列表,数据可被查询,但不能被修改,类似于列表的切片操作,元组写在小括号里面()元素之前用逗号隔开 对于一些不想被修改的数据,可以用元组来保存 #  创建元组 1)创建空元组 # 创建空元 ...

  9. 洛谷(P1006 传纸条)

    题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个mm行nn列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运 ...

  10. SapScript

    * [OPEN_FORM] SAPscript: フォーム印刷の開始 * [START_FORM] SAPscript: 書式を開始 * [WRITE_FORM] SAPscript: 書式ウィンドウ ...