前言:趁着对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算法(堆优化)的更多相关文章

  1. 单源最短路径Dijkstra算法,多源最短路径Floyd算法

    1.单源最短路径 (1)无权图的单源最短路径 /*无权单源最短路径*/ void UnWeighted(LGraph Graph, Vertex S) { std::queue<Vertex&g ...

  2. 单源最短路径——dijkstra算法

    dijkstra算法与prim算法的区别   1.先说说prim算法的思想: 众所周知,prim算法是一个最小生成树算法,它运用的是贪心原理(在这里不再证明),设置两个点集合,一个集合为要求的生成树的 ...

  3. 单源最短路径 dijkstra算法实现

    本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图.而且连通,有向图,不连通图的做法相似. 算法简述: 首先确定"单源"的源.假设是第0个顶点. 维护三个数组 ...

  4. 单源最短路径-Dijkstra算法

    1.算法标签 贪心 2.算法描述 具体的算法描述网上有好多,我觉得莫过于直接wiki,只说明一些我之前比较迷惑的. 对于Dijkstra算法,最重要的是维护以下几个数据结构: 顶点集合S : 表示已经 ...

  5. 单源最短路径——Dijkstra算法学习

    每次都以为自己理解了Dijkstra这个算法,但是过没多久又忘记了,这应该是第4.5次重温这个算法了. 这次是看的胡鹏的<地理信息系统>,看完之后突然意识到用数学公式表示算法流程是如此的好 ...

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

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

  7. luogu P3371 & P4779 单源最短路径spfa & 最大堆优化Dijkstra算法

    P3371 [模板]单源最短路径(弱化版) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出 ...

  8. [数据结构与算法-15]单源最短路径(Dijkstra+SPFA)

    单源最短路径 问题描述 分别求出从起点到其他所有点的最短路径,这次主要介绍两种算法,Dijkstra和SPFA.若无负权优先Dijkstra算法,存在负权选择SPFA算法. Dijkstra算法 非负 ...

  9. Dijkstra算法堆优化

    转自 https://blog.csdn.net/qq_41754350/article/details/83210517 再求单源最短路径时,算法有优劣之分,个人认为在时间方面 朴素dijkstra ...

  10. matlab练习程序(单源最短路径Dijkstra)

    图的相关算法也算是自己的一个软肋了,当年没选修图论也是一大遗憾. 图像处理中,也有使用图论算法作为基础的相关算法,比如图割,这个算法就需要求最大流.最小割.所以熟悉一下图论算法对于图像处理还是很有帮助 ...

随机推荐

  1. (js描述的)数据结构[字典](7)

    (js描述的)数据结构[字典](7) 一.字典的特点 1.字典的主要特点是一一对应关系. 2.使用字典,剋通过key取出对应的value值. 3.字典中的key是不允许重复的,而value值是可以重复 ...

  2. linux系统管理,查看系统资源

    free 查看内存使用情况 -b  ===>  以byte为单位 -k  ===>  以Kb为单位 -m  ===>  以Mb为单位 -g  ===>  以Gb为单位 -t  ...

  3. Linux 磁盘管理篇(一 磁盘分区)

    显示系统所有分区内容            fdisk 分区工具                parted fdisk: 执行完后按下 q 是退出不保存操作的意思 执行完后按下 w 是执行操作的意思 ...

  4. "高亮显示"组件:<mark> —— 快应用组件库H-UI

     <import name="mark" src="../Common/ui/h-ui/text/c_tag_mark"></import& ...

  5. 查看jdk 线程 日志

    命令:jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令 这些命令 必须 在 linux jdk bin 路径 下执行 eq: ./jstack 10303 即可  如果想把 ...

  6. FastAPI框架

    目录 FastAPI框架 安装 基本使用 模版渲染 安装jinja2 基本使用 form表单数据交互 基本数据 文件交互 静态文件配置 FastAPI框架 该框架的速度(天然支持异步)比一般的djan ...

  7. android学习笔记——利用BaseAdapter生成40个列表项

    RT: main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns ...

  8. 5分钟python爬虫案例,手把手教爬取国内外最新疫情历史数据

    俗话说的好,“授之以鱼不如授之以渔”,所以小编今天就把爬疫情历史数据的方法分享给你们. 基本思路:分析腾讯新闻“抗肺炎”版块,采用“倒推法”找到疫情数据接口,然后用python模拟请求,进而保存疫情历 ...

  9. 简单网络编程如何用python来实现

    对于网络编程,通信模式是后台必备技能,先用最基础代码实现,理解一些 API 的含义,在深入学习. 总是有读者问过我关于 Python 后台开发相关,如果想走 Python 后台方向,对于 Python ...

  10. HPU第一次团队赛

    D. Tom的战力问题 Tom被斯派克揍了TAT.Tom下定决心要战胜斯派克.但是在战胜最强的斯派克之前,Tom要先打败其他的狗.为此,他打算先收集一下信息.现在Tom在了得到了一些关于战斗力的小道消 ...