单源最短路径:Dijkstra算法(堆优化)
前言:趁着对Dijkstra还有点印象,赶快写一篇笔记。
注意:本文章面向已有Dijkstra算法基础的童鞋。
简介
单源最短路径,在我的理解里就是求从一个源点(起点)到其它点的最短路径的长度。
当然,也可以输出这条路径,都不是难事。
但是,Dijkstra不能处理有负权边的图。
解析
注:接下来,我们的源点均默认为1。
先上代码(注意,是堆优化过的!!):
struct node{
int id;
int total;
node(){};
node(int Id,int Total){
id=Id;
total=Total;
}
bool operator < (const node& x) const{
return total>x.total;
}
};
void dijkstra(int start){
memset(dis,inf,sizeof(dis));
memset(conf,false,sizeof(conf));
memset(pre,0,sizeof(pre));
dis[start]=0;
priority_queue <node> Q;
Q.push(node(1,0));
while(Q.size()){
int u=Q.top().id;
Q.pop();
if(conf[u])
continue;
conf[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
int cost=dis[u]+e[i].w;
if(cost < dis[v]){
dis[v]=cost;
pre[v]=u;
Q.push(node(v,dis[v]));
}
}
}
}
接下来,一步一步解析代码:
首先是结构体node
struct node{
int id;
int total;
node(){};
node(int Id,int Total){
id=Id;
total=Total;
}
bool operator < (const node& x) const{
return total>x.total;
}
};
这里的id就是这个结点的编号,total就是走到当前节点的最小花费。
构造函数就不用我多说了吧。
因为在原始的Dijkstra中,每次都要选出当前花费最小的那个点,如果采用堆优化,使得堆头永远都是花费最小的那个,这样每次选出花费最小的那个点的时间复杂度从\(O(n)\)骤降到\(O(logn)\)。
如果要用到堆,就可以使用STL的优先队列(priority_queue)。
因为优先队列默认是优先级最高的放在最前面,在Dijkstra中,优先级就是这个node的total,total越小优先级就越高。
因为total越大,优先级越低,所以这里的小于运算符就可以定义为total>x.total。
接下来是初始化
memset(dis,inf,sizeof(dis));
memset(conf,false,sizeof(conf));
memset(pre,0,sizeof(pre));
dis[start]=0;
Q.push(node(1,0));
数组dis[i]表示的是从源点到点i的最短路的长度,初始时不知道能不能到达,设为inf(无穷大)。
数组conf[i]表示的是点i的最短路径是否确认,若是,则为true,否则为false。
数组pre[i]表示的是点i的前驱,即到点i的前一个点的编号。
例如有一条最短路径是这样的:1->3->8->5->2,那么pre[2]=5;pre[5]=8;pre[8]=3;。
这样一来,输出路径就好办了:
//假设要输出到2的路径
int i=2;
while(pre[i]!=1){
ans.push(i);
i=pre[i];
}
printf("1");
while(!ans.empty()){
printf("->%d",ans.top());
ans.pop();
}
此外,一开始从结点1出发,到结点1的距离为0,知道这些信息后,将源点入堆。
Q.push(node(1/*节点编号*/,0/*到该节点距离*/));
接下来是重点了,我们再次一步步地拆分:
int u=Q.top().id;
Q.pop();
if(conf[u])
continue;
conf[u]=true;
这个应该不难理解,首先拿出一个源点u,u的编号自然是Q.top().id。接下来Q.pop()必不可少。
这时候,如果conf[u]==true,即结点u的最短路长度已经确定过了,那就没必要再走了,因为之前肯定走过了。直接continue看下一个结点。
如果没有,按照Dijkstra的特性,当前结点u的总路径长度肯定是最短了,那么就被确定了,conf[u]=true。
然后是下一段:
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
int cost=dis[u]+e[i].w;
if(cost < dis[v]){
dis[v]=cost;
pre[v]=u;
Q.push(node(v,dis[v]));
}
}
这段其实好理解,不过我用的是链式前向星存图,如果你用的是vector做的邻接表,其实大体上是相同的。
如果你用的是邻接表或邻接矩阵,这里的v其实就是当前找的这条路的终点(e[i].v表示的是这条边的终点。
而cost,则是dis[u]的值加上这条边的权值(没错,e[i].w表示的是这条边的权值),也就是到点v的总花费。
如果cost<dis[v],即当前这条路到v的总花费比原来到v的总花费小,就可以更新了:
dis[v]=cost;
pre[v]=u;
Q.push(node(v,dis[v]));
首先是总花费更新,然后再更新前驱,最后把这个到过的点放入优先队列里。
至此,堆优化Dijkstra就结束了。
但是有一个比较关心的点:时间复杂度是多少呢?
首先考虑有哪些结点会被搜索:
显然是一开始conf[u]==false的结点,而一点出堆之后,conf[u]=true,所以有n个节点会被搜索同时入队,每次入队需要\(O(logn)\)。
接下来是遍历每个结点的边,如果用\(E_i\)表示和结点\(i\)相邻的边的数量,显然有:\(\sum_{i=1}^n E_i = m\),在最坏情况下,每次搜索边的时候都要入队一次,那么总时间复杂度就是:\(O(mlogn)\)。
完结撒花✿
单源最短路径:Dijkstra算法(堆优化)的更多相关文章
- 单源最短路径Dijkstra算法,多源最短路径Floyd算法
1.单源最短路径 (1)无权图的单源最短路径 /*无权单源最短路径*/ void UnWeighted(LGraph Graph, Vertex S) { std::queue<Vertex&g ...
- 单源最短路径——dijkstra算法
dijkstra算法与prim算法的区别 1.先说说prim算法的思想: 众所周知,prim算法是一个最小生成树算法,它运用的是贪心原理(在这里不再证明),设置两个点集合,一个集合为要求的生成树的 ...
- 单源最短路径 dijkstra算法实现
本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图.而且连通,有向图,不连通图的做法相似. 算法简述: 首先确定"单源"的源.假设是第0个顶点. 维护三个数组 ...
- 单源最短路径-Dijkstra算法
1.算法标签 贪心 2.算法描述 具体的算法描述网上有好多,我觉得莫过于直接wiki,只说明一些我之前比较迷惑的. 对于Dijkstra算法,最重要的是维护以下几个数据结构: 顶点集合S : 表示已经 ...
- 单源最短路径——Dijkstra算法学习
每次都以为自己理解了Dijkstra这个算法,但是过没多久又忘记了,这应该是第4.5次重温这个算法了. 这次是看的胡鹏的<地理信息系统>,看完之后突然意识到用数学公式表示算法流程是如此的好 ...
- 最短路模板(Dijkstra & Dijkstra算法+堆优化 & bellman_ford & 单源最短路SPFA)
关于几个的区别和联系:http://www.cnblogs.com/zswbky/p/5432353.html d.每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个(草儿家到 ...
- luogu P3371 & P4779 单源最短路径spfa & 最大堆优化Dijkstra算法
P3371 [模板]单源最短路径(弱化版) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出 ...
- [数据结构与算法-15]单源最短路径(Dijkstra+SPFA)
单源最短路径 问题描述 分别求出从起点到其他所有点的最短路径,这次主要介绍两种算法,Dijkstra和SPFA.若无负权优先Dijkstra算法,存在负权选择SPFA算法. Dijkstra算法 非负 ...
- Dijkstra算法堆优化
转自 https://blog.csdn.net/qq_41754350/article/details/83210517 再求单源最短路径时,算法有优劣之分,个人认为在时间方面 朴素dijkstra ...
- matlab练习程序(单源最短路径Dijkstra)
图的相关算法也算是自己的一个软肋了,当年没选修图论也是一大遗憾. 图像处理中,也有使用图论算法作为基础的相关算法,比如图割,这个算法就需要求最大流.最小割.所以熟悉一下图论算法对于图像处理还是很有帮助 ...
随机推荐
- 给Jekyll静态博客添加ScrollSpy博文大纲目录
目录 内置TOC 添加ScrollSpy博文menu Scrollnav.js 使用方法❤ 最近又双叒把博客模板换成了Jekyll,Jekyll无论上手难度和修改难度都是目前所见流行模板中最低的(以无 ...
- 了解这5大K8S管理服务,为你节省50%的部署时间!
Kubernetes已然成为IT世界的重要组成部分,并且仍在不断地发展壮大,现阶段,Kubernetes已经可以帮助企业进行微服务训练,加速企业数字化转型.尽管Kubernetes是一款如此令人印象深 ...
- Bat 脚本学习 (基础篇)
[转]Bat 脚本学习 2015-01-05 14:13 115人阅读 评论(0) 收藏 举报 基础部分: ============================================== ...
- dubbo(三):负载均衡实现解析
dubbo作为分布式远程调用框架,要保证的点很多,比如:服务注册与发现.故障转移.高性能通信.负载均衡等等! 负载均衡的目的是为了特定场景下,能够将请求合理地平分到各服务实例上,以便发挥所有机器的叠加 ...
- Berry Jam codeforces 1278C
题目大意: 有两种类型的果酱,一个梯子,从中间开始吃,可以吃左边的,也可以吃右边的,最终要使两种类型的果酱的数量想等 题解: 思路对了,但是没考虑完. 对梯子的左侧的果酱I我们用两个数组记录其从1到i ...
- E - Farthest Nodes in a Tree
Given a tree (a connected graph with no cycles), you have to find the farthest nodes in the tree. Th ...
- 二分例题 51nod
例题1 1010 只包含因子2 3 5的数 http://www.51nod.com/Challenge/Problem.html#problemId=1010 K的因子中只包含2 3 5.满足条件的 ...
- [HarekazeCTF2019] web
在 buuoj 上看到的这个比赛题目,期间平台关了,就拿了 Dockerfile 本地做了,web 题目感觉还不错 encode_and_encode [100] 打开靶机,前两个页面都是 html ...
- [javascript] js实现小数的算术运算方法
/** ** 加法函数,用来得到精确的加法结果 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果. ** 调用:accAdd(arg ...
- kubernetes的无状态服务和有状态服务介绍
无状态服务 1)是指该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的 2)多个实例可以共享相同的持久化数据.例如: nginx实例和tomcat实例 3 ...