Dijkstra算法用于解决单源最短路径问题,通过逐个收录顶点来确保已收录顶点的路径长度为最短。

    

图片来自陈越姥姥的数据结构课程:https://mooc.study.163.com/learn/1000033001?tid=1000044001#/learn/content?type=detail&id=1000112011&cid=1000126096

Dijkstra算法的时间复杂度,取决于“V=未收录顶点中dist最小者”的算法。这一步可以用线性查找实现,也可以用最小堆实现。

线性查找的算法就不用多说了。最小堆的算法有一个问题:最小堆是以未收录顶点的dist作为key来建立的,但是每一轮循环都会把部分顶点的dist值改变,也就会破坏最小堆的有序性,怎么解决?

显然应该在每一轮循环中把最小堆重新调整成有序。现在问题又来了:

1. 复杂度还合算吗?

建堆的时间复杂度是O(N),pop一个元素的时间复杂度是O(logN);线性查找的时间复杂度也是O(N)。建堆还额外使用了O(N)的空间。

看似一点都不合算。但我又想到每一轮循环中的建堆操作,很可能只需要调整少量元素,而对于其他元素,只需要进行访问。然而线性查找连调整都不需要,只有交换。再然而,循环过程中堆会变小,使建堆的时间复杂度中的常数变小。至于到底哪个更快,还得实践出真知。

所以只能从消除建堆操作入手。这样又是另一种算法了,参考资料[1]给出了详细说明,这种算法中每一轮的时间复杂度为O(logN),总时间复杂度为O(|E|log|V|)(V为顶点,E为边)。

2. 如何利用STL进行堆操作?

STL <algorithm> 头文件提供了 std::is_heap 、 std::is_heap_until (这两个需要C++11)、 std::make_heap 、 std::push_heap 、 std::pop_heap 和 std::sort_heap 等函数模板用于堆操作。

现有一道单源最短路径的题:https://pintia.cn/problem-sets/994805342720868352/problems/994805523835109376,Dijkstra算法的变形而已。

以下为实现代码。三种算法用宏定义选择,已选择优先队列算法。

 #include <iostream>
#include <limits>
#include <vector>
#include <queue>
#include <algorithm>
#include <utility>
#include <functional> //#define LINEAR
//#define HEAP
#define QUEUE struct Path
{
Path() = default;
Path(int _city, int _dist)
: city(_city), dist(_dist)
{
;
}
int city;
int dist;
bool operator<(const Path& _rhs) const
{
return dist < _rhs.dist;
}
bool operator>(const Path& _rhs) const
{
return dist > _rhs.dist;
}
}; struct City
{
std::vector<Path> paths;
int team;
int dist = std::numeric_limits<int>::max();
bool collected = false;
int team_max = ;
int dist_count = ;
}; #ifdef HEAP
class Comparator
{
public:
Comparator(std::vector<City>& _cities)
: cities_(&_cities)
{
;
}
bool operator()(int _lhs, int _rhs)
{
return (*cities_)[_lhs].dist > (*cities_)[_rhs].dist;
}
private:
std::vector<City>* cities_;
};
#endif int main()
{
int n, m, src, dst;
std::cin >> n >> m >> src >> dst;
std::vector<City> cities(n);
for (auto& city : cities)
std::cin >> city.team;
for (int cnt = ; cnt != m; ++cnt)
{
int src, dst, dist;
std::cin >> src >> dst >> dist;
cities[src].paths.emplace_back(dst, dist);
cities[dst].paths.emplace_back(src, dist);
} {
auto& city = cities[src];
cities[src].collected = true;
cities[src].dist = ;
cities[src].dist_count = ;
cities[src].team_max = cities[src].team;
}
#ifdef QUEUE
std::priority_queue<Path, std::vector<Path>, std::greater<Path>> queue;
#endif
for (const auto& path : cities[src].paths)
{
cities[path.city].dist = path.dist;
cities[path.city].dist_count = ;
cities[path.city].team_max = cities[src].team + cities[path.city].team;
#ifdef QUEUE
queue.emplace(path.city, path.dist);
#endif
} #ifdef HEAP
std::vector<int> heap;
heap.reserve(n - );
for (int i = ; i != n; ++i)
if (i != src)
heap.push_back(i);
Comparator comp(cities);
std::make_heap(heap.begin(), heap.end(), comp);
#endif while ()
{
#ifdef LINEAR
int min_dist = std::numeric_limits<int>::max();
int index = -;
for (int i = ; i != n; ++i)
if (!cities[i].collected && cities[i].dist < min_dist)
min_dist = cities[i].dist, index = i;
if (index == -)
break;
auto& city = cities[index];
#endif
#ifdef HEAP
if (heap.empty())
break;
auto& city = cities[heap[]];
#endif
#ifdef QUEUE
if (queue.empty())
break;
Path temp;
while ()
{
temp = queue.top();
queue.pop();
if (!cities[temp.city].collected)
break;
}
auto& city = cities[temp.city];
#endif
city.collected = true;
for (const auto& path : city.paths)
{
if (!cities[path.city].collected)
{
auto& dest = cities[path.city];
if (city.dist + path.dist < cities[path.city].dist)
{
dest.dist = city.dist + path.dist;
dest.dist_count = city.dist_count;
dest.team_max = city.team_max + dest.team;
}
else if (city.dist + path.dist == cities[path.city].dist)
{
dest.dist = city.dist + path.dist;
dest.dist_count += city.dist_count;
if (city.team_max + dest.team > dest.team_max)
dest.team_max = city.team_max + dest.team;
}
#ifdef QUEUE
queue.emplace(path.city, dest.dist);
#endif
}
}
#ifdef LINEAR
if (index == dst)
break;
#endif
#ifdef HEAP
if (heap[] == dst)
break;
std::pop_heap(heap.begin(), heap.end(), comp);
heap.pop_back();
std::make_heap(heap.begin(), heap.end(), comp);
#endif
#ifdef QUEUE
if (temp.city == dst)
break;
#endif
} {
auto& city = cities[dst];
std::cout << cities[dst].dist_count << ' ' << cities[dst].team_max;
} return ;
}

测试结果:

线性查找版

最小堆版

优先队列版

平台显示线性查找版的时间6ms,内存512KB;最小堆版的时间5ms,内存512KB;优先队列版的时间3ms,内存424KB。我认为时间都太短了,数据量不够大,不足以说明问题。

如果仅从理论上分析的话,我认为优先队列的算法是最优的。

参考资料:

[1] dijkstra + heap 优化 https://blog.csdn.net/sentimental_dog/article/details/51955765

Dijkstra算法与堆(C++)的更多相关文章

  1. dijkstra算法的堆优化

    普通的dijkstra算法模板: //数据结构 int g[LEN][LEN]; //邻接矩阵 int vis[LEN]; //标记是否访问 int dist[LEN] //源点到各点的距离 fill ...

  2. 单源最短路径:Dijkstra算法(堆优化)

    前言:趁着对Dijkstra还有点印象,赶快写一篇笔记. 注意:本文章面向已有Dijkstra算法基础的童鞋. 简介 单源最短路径,在我的理解里就是求从一个源点(起点)到其它点的最短路径的长度. 当然 ...

  3. 单源最短路问题 Dijkstra 算法(朴素+堆)

    选择某一个点开始,每次去找这个点的最短边,然后再从这个开始不断迭代,更新距离. 代码: 朴素(vector存图) #include <iostream> #include <cstd ...

  4. 【Luogu P4779】dijkstra算法的堆优化

    Luogu P4779 利用堆/优先队列快速取得权值最小的点. 在稠密图中的表现比SPFA要优秀. #include<iostream> #include<cstdio> #i ...

  5. 基于STL优先队列和邻接表的dijkstra算法

    首先说下STL优先队列的局限性,那就是只提供入队.出队.取得队首元素的值的功能,而dijkstra算法的堆优化需要能够随机访问队列中某个节点(来更新源点节点的最短距离). 看似可以用vector配合m ...

  6. 最短路径问题---Floyed(弗洛伊德算法),dijkstra算法,SPFA算法

    在NOIP比赛中,如果出图论题最短路径应该是个常考点. 求解最短路径常用的算法有:Floyed算法(O(n^3)的暴力算法,在比赛中大概能过三十分) dijkstra算法 (堆优化之后是O(MlogE ...

  7. 三角网格上的寻路算法Part.1—Dijkstra算法

    背景 最近在研究中产生了这样的需求:在三角网格(Mesh)表示的地形图上给出两个点,求得这两个点之间的地面距离,这条距离又叫做"测地线距离(Geodesic)".计算三角网格模型表 ...

  8. 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)

    关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...

  9. Dijkstra算法的二叉堆优化

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

随机推荐

  1. 毕业设计之感悟 —— UML 与 ER 图

    今天毕业设计答辩,虽然我第一个上场,但是不是特别紧张,因为整个系统都是我写的.我以为自己天衣无缝,能应付所有老师的所有问题.事实上,我被老师教育了一番. 老师说我,毕业论文中没有一个类.我一开始比较懵 ...

  2. C# 读取大文件 (可以读取3GB大小的txt文件)

    原文:C# 读取大文件 (可以读取3GB大小的txt文件) 在处理大数据时,有可能 会碰到 超过3GB大小的文件,如果通过 记事本 或 NotePad++去打开它,会报错,读不到任何文件. 如果你只是 ...

  3. PHP XDebug Sublime Text 单步调试

    前置环境:已经安装好LNMP 1. 安装xdebug 可以通过pear包管理来安装 sudo apt-get install php-pear sudo pecl install xdebug 这里我 ...

  4. Win10《芒果TV》商店版更新v3.2.5:新增会员频道,修复多处细节问题,小年快乐

    听因乐不凡,尽在芒果TV,湖南卫视大型音乐竞技节目<歌手>,每周六晚22:30在芒果TV与湖南卫视同步直播,1月20日周五晚七点半,2016-2017湖南卫视<小年夜春晚>会员 ...

  5. SqlServer删除复制监视器中无效的发布名称

    原文:SqlServer删除复制监视器中无效的发布名称 在服务器复制监视器中有一个发布名称,因为该发布订阅已经删除. ReportServerTempDB只有一个发布,已无效,打算删除. --直接删除 ...

  6. MySQL 常用数据存储引擎区别

    mysql有多种存储引擎,目前常用的是 MyISAM 和 InnoDB 这两个引擎,除了这两个引擎以为还有许多其他引擎,有官方的,也有一些公司自己研发的.这篇文章主要简单概述一下常用常见的 MySQL ...

  7. C#跳转语句

    1.break 退出直接封闭它的switch.while.do.for或foreach语句. 当有嵌套时,break只退出最里层的语句块. break不能跳出finally语句块. 2.continu ...

  8. xgboost参数及调参

    常规参数General Parameters booster[default=gbtree]:选择基分类器,可以是:gbtree,gblinear或者dart.gbtree和draf基于树模型,而gb ...

  9. 在Azure中搭建Ghost博客并绑定自定义域名和HTTPS

    绪论 之前一直使用cnblog写博客,现在将博客迁移至Microsoft Azure上的Ghost博客上,Ghost博客使用Markdown书写博客,页面简洁,是我喜欢的风格.具体参见官网:https ...

  10. 学会了使用qmake -query

    D:\Qt\Qt5.6.2_static_kk\bin>qmake -queryQT_SYSROOT:QT_INSTALL_PREFIX:C:/Qt/Qt5.6.2_static_kkQT_IN ...