第十七章 优先队列优化Dijkstra算法

这篇具有很好参考价值的文章主要介绍了第十七章 优先队列优化Dijkstra算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、普通dijkstra算法的缺陷

作者在这里建议,不太懂dijkstra算法的同学可以去看看作者对该算法的详细讲解以及通俗证明,这样大家就能够体会到原算法的缺陷。
传送门:第十六章 Dijkstra算法的讲解以及证明(与众不同的通俗证明)

1、选出最小距离的过程:

     int t=-1;
     for(int j=1;j<=n;j++)
     {
         if(!s[j]&&(t==-1||dis[j]<dis[t]))t=j;
     }

我们的dijkstra算法会选出所有松弛后所得距离的最小值。而我们之前的代码是遍历所有点来确定的,时间复杂度是O(N)。那么取出n个最小的点,时间复杂度就是O(N2)。

但是我们知道,我们在一堆数中选出一个最小值,只需要使用我们之前学过的数据结构:堆。假设我们选出一个最小值,那么堆选出这个值的时间复杂度是O(logN)。由于我们要求所有的点的最短路,所以我们要取出n个点,那么取出n个点的时间复杂度很多人认为是O(nlogn),其实这是一个很大的误区,因为随着你取出的点的增多,你的堆的高度在减少,那么我们取出n个最小的点需要的时间复杂度是多少呢?

取出n个数的时间复杂度其实就是插入n个数的时间复杂度,并不是nlogn,而是O(N)
推导如下:
第十七章 优先队列优化Dijkstra算法

数据结构堆就是STL中的优先队列:priority_queue

2、松弛所有点的过程:

        
        for(int j=1;j<=n;j++)
        {
            dis[j]=min(dis[j],g[t][j]+dis[t]);
        }

通过我们第十五章对dijkstra算法的证明,我们发现,每次松弛过程得到有效更新的点,都是我们中间点的邻接点。也就是说,我们只需要去松弛该点的邻接点即可,不用去松弛所有点。因此,我们只需要去松弛该点的邻接点。那么这个时候,我们用邻接表就会便于我们去访问了。因为邻接表中将一个点的所有邻接点都记录在了链表中。我们只需要遍历对应点的链表即可。

二、如何优化

1、代码模板

(1)问题:

第十七章 优先队列优化Dijkstra算法

(2)模板:

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e6+10;
int h[N],e[N],ne[N],idx,w[N];
bool s[N];
int dis[N];
int n,m,a,b,c;
void add(int a,int b,int c)
{
    e[idx]=b;ne[idx]=h[a];w[idx]=c;h[a]=idx++;
}
int dijkstra()
{
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
    q.push(make_pair(0,1));
    while(!q.empty())
    {
        auto t=q.top();
        q.pop();
        if(s[t.second])continue;
        s[t.second]=true;

        for(int i=h[t.second];i!=-1;i=ne[i])
        {
            if(dis[e[i]]>t.first+w[i])
            {
                dis[e[i]]=t.first+w[i];
                q.push({dis[e[i]],e[i]});
            }
        }
    }
    if(dis[n]==0x3f3f3f3f)return -1;
    else return dis[n];
}
int main()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    while(m--)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    cout<<dijkstra();
    return 0;
}

2、详细解读

第十七章 优先队列优化Dijkstra算法

三、优化分析

1、使用条件:

我们这道题中的图是稀疏图,所以我们可以使用邻接表,从而优化这个代码。但是要是稠密图的话,我们可能还是需要用邻接矩阵来存储,这样的话一般使用的是朴素的dijkstra算法。

2、常见问题:

(1)重边的处理

我们的邻接矩阵存储图的时候,会通过min函数存储重边中的最小边。但是我们的邻接表会存储所有重边。这其实是没关系的。比如我们a,b点内存储了3,1,2。三个长度的边。一开始可能会让3+x这个距离进堆,但是我们当再次遍历到1这个边,松弛成功后我们就会让1+x进堆。而1+x存在后,3+x就不会成堆顶,所以就不会对结果产生影响。那么当我们的1+x出堆后,说明这个点的最短路已经确定了。那么我们直接标记一下,所以当1+x删除后,轮到3+x出堆的时候,由于我们已经标记了。所以通过continue就可以直接删除3+x这个数据。而后续的2+x也会在轮到它出堆的时候,删除掉。

(2)时间复杂度

寻找路径最短的点(取出n个最小值的过程):O(n)

加入集合ST:O(n)

更新距离:O(mlogn)

所以总的时间复杂度是:O(mlogn)文章来源地址https://www.toymoban.com/news/detail-425038.html

到了这里,关于第十七章 优先队列优化Dijkstra算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第十七章行为性模式—状态模式

    行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式: 类行为模式:采用继承机制来在类间分派行为 对象行为模式:

    2024年02月07日
    浏览(32)
  • 第十七章 Unity 预制件prefab(下)

    本章节我们来讲解如何编辑预制体文件。这里介绍三种打开编辑预制件的方式。第一就是通过预制件的实例游戏对象的Inspector检视面板上面的预制件“打开”按钮。 第二就是在Project工程面板中选中预制件文件(Cube.prefab),然后在Inspector检视面板中点击“打开预制件”。 第

    2024年02月04日
    浏览(23)
  • 离散数学复习---第十七章 平面图【概念版】

    目录 17.1 平面图的基本概念 17.2  欧拉公式 17.3  平面图的判断 17.4  平面图的对偶图 定义17.1   如果能将无向图G画在平面上使得除顶点外处处无边相交,则称G为 可平面图 ,简称为 平面图 。画出的无边相交的图称为G的 平面嵌入 。无平面嵌入的图称为 非平面图 。 定理17.

    2024年02月05日
    浏览(28)
  • 《微服务实战》 第十七章 Redis下载与安装

    第二十八章 分布式锁框架-Redisson 第二十四章 Spring boot 操作 Redis 第二十三章 Redis RDB AOF 第二十一、二十二章 Redis发布订阅、事务;HyperLoglog基数统计 第二十章 Redis连接指令 客户端指令 服务器指令 第十九章 Redis key 第十八章 Redis查看配置文件和数据类型 第十七章 Redis下载与安

    2024年02月06日
    浏览(28)
  • Hotspot源码解析-第十七章-虚拟机万物创建(三)

    分配Java堆内存前,我们先通过两图来了解下C堆、Java堆、内核空间、native本地空间的关系。 1、从图17-1来看,Java堆的分配其实就是从Java进程运行时堆中选中一块内存区域来映射 2、从图17-2,可以看中各内存空间的关系,当然实际的内存区域比这个复杂的多,这里只是概括说

    2024年01月25日
    浏览(27)
  • 【新版系统架构】第十七章-通信系统架构设计理论与实践

    软考-系统架构设计师知识点提炼-系统架构设计师教程(第2版) 第一章-绪论 第二章-计算机系统基础知识(一) 第二章-计算机系统基础知识(二) 第三章-信息系统基础知识 第四章-信息安全技术基础知识 第五章-软件工程基础知识(一) 第五章-软件工程基础知识(需求工

    2024年02月15日
    浏览(34)
  • Go学习第十七章——Gin中间件与路由

    Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等 即比如,如果访问一个网页的话,不管访问什么路径都需要进行登录,

    2024年02月07日
    浏览(35)
  • 【Rust】Rust学习 第十七章Rust 的面向对象特性

    面向对象编程(Object-Oriented Programming,OOP)是一种模式化编程方式。对象(Object)来源于 20 世纪 60 年代的 Simula 编程语言。这些对象影响了 Alan Kay 的编程架构中对象之间的消息传递。他在 1967 年创造了  面向对象编程  这个术语来描述这种架构。关于 OOP 是什么有很多相互矛

    2024年02月11日
    浏览(32)
  • TCP/IP网络编程 第十七章:优于select的epoll

    select复用方法其实由来已久,因此,利用该技术后,无论如何优化程序性能也无法同时接入上百个客户端(当然,硬件性能不同,差别也很大)。这种select方式并不适合以Web服务器端开发为主流的现代开发环境,所以要学习Linux平台下的epoll。 基于select的I/O复用技术速度慢的原

    2024年02月16日
    浏览(28)
  • 【vue2第十七章】VueRouter 编程式导航跳转传参(点击按钮跳转路由和如何传递参数)

    如何在js进行跳转路由 在一些需求中,我们需要不用点击a标签或者router-link,但是也要实现路由跳转,比如登陆,点击按钮搜索跳转。那么这种情况如何进行跳转呢? 直接再按钮绑定的方法中写 this.$router.push(\\\'路由路径\\\') 即可。 代码示范 this.$router.push(\\\"/跳转路径\\\") 或者 this

    2024年02月09日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包