你真的掌握到“优先级队列“的精髓了吗?

前文

如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了。这样,我们就引入了 优先级队列 这种 数据结构。

一,priority_queue的介绍

1.优先级队列是一种 容器适配器,根据严格的弱排序标准,他的第一个元素是所有元素中最大的。
2.此结构类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
3.优先队列被实现为 容器适配器容器适配器即将特定容器类封装作为其底层容器类,priority_queue 提供一组特定的成员函数来访问其元素。元素从 特定容器的“尾部”弹出,其称为优先队列的顶部
4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5.标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
6.需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

二,priority_queue的模拟实现

priority_queue要实现的功能有以下几种:

void adjust_up(int child)//push的时候要用
void adjust_down(int parent)//pop的时候要用
void push(const T& x)//在优先级队列中插入元素x
void pop()//删除优先级队列中最大(最小)元素,即堆顶元素
const T& top()//返回优先级队列中最大(最小元素),即堆顶元素
size_t size()//返回队列中的有效数据个数
bool empty()//判空
由于底层结构是堆的顺序结构,所有实际上这个模拟实际上就是 利用底层容器实现一个堆.

2.1 成员变量

template<class T,class Container=vector<T>,class Compare> 
    class priority_queue
    {
    private:
        Container _con;
    };
这里底层容器的实现我们默认用 vector,这里需要 注意的一点是我们这里不需要写构造函数,默认构造函数会自动调用底层容器的构造函数

2.2 adjust_up()/adjust_down()

我们在简介中提到优先级队列用的堆的结构,默认情况下用的是大堆,而用堆的话,在push和pop的过程中就少不了向上调整和向下调整,因此我们先将这两个函数完成,其他的就很简单。

2.2.1 adjust_up()

adjust_up向上调整,通常用于push,在结尾处push一个值后,通过向上调整,将他调整到该去的位置,这里我们采取的是图文讲解的模式,下面就上图
void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child>0)
            {
                if (_con[parent] < _con[child])
                {
                    swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

2.2.2 adjust_down()

ajust_down向下调整,用于pop,在pop时会解释
adjust_down需要考虑左右孩子的越界问题,这里需要注意
//向下调整
        void adjust_down(int parent)
        {
            int child = parent * 2 + 1;
            while (child < _con.size())
            {
                //看右孩子是否越界,然后取左右孩子大的值
                if (child + 1 < _con.size() && _con[child + 1] > _con[child])
                {
                    child = child + 1;
                }

                if (_con[parent] < _con[child])
                {
                    swap(_con[parent], _con[child]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }

2.3 push()/pop()

2.3.1 push()

push:在优先级队列中插入元素x
实现逻辑:尾插插入元素x,然后通过adjust_up调整位置,堆的结构不被破坏。
        void push(const T& x)
        {
            _con.push_back(x);
            adjust_up(_con.size()-1);
        }

2.3.2 pop()

pop:删除优先级队列中最大(最小)元素,即堆顶元素
实现逻辑:队列中的 top最后一个数据进行 交换,然后进行 尾删,在对换到top位置的数据进行adjust_down,保证堆的 结构不被破坏
        void pop()
        {
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            adjust_down(0);
        }

2.4 top()/size()/empty()

这三个接口的实现比较简单,直接用底层容器的接口实现
        const T& top()
        {
            return _con[0];
        }
        size_t size()
        {
            return _con.size();
        }
        bool empty()
        {
            return _con.empty();
        }

三,priority_queue的精髓

priority_queue的大体框架大致如上,但这并不是它的精髓,那么他的精髓是什么呢?
不知道有没有老铁发现priority_queue的模板参数有三个,而上面的内容只用了前两个,最后一个还没有用,而最后一个就是精髓。
那么它有什么作用呢,众所周知堆分大堆和小堆,大堆top是最大值,小堆top是最小值,而priority_queue也是支持这种转变的,如何实现的呢,就是靠第三个模板参数。
如上图所示就是库中priority_queue第三个参数转为小堆的使用方法,如上可知greater也是一个类,因为他需要传类模板参数,那么greater是什么呢?
这里还需要涉及一个知识点,那就是仿函数。

3.1 仿函数

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator (),这个类就有了类似函数的行为,就是一个仿函数类了。
需要注意的是,仿函数也需要实例化.

3.2 构建greater()/less()

greater()/less()的构建和上面类似,不过多了一个模板参数
    template <class T>
    class less//less小于是我们按照库里的来的
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

    template <class T>
    class greater
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };
而运用less、greater需要注意,less在库中表示的是大堆,而我们想要使用就需要调整位置,调整成<。
然后就可以套用类函数。
    void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child>0)
            {
                if (_com(_con[parent],_con[child]))//类函数
                {
                    swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        //向下调整
        void adjust_down(int parent)
        {
            int child = parent * 2 + 1;
            while (child < _con.size())
            {
                //看右孩子是否越界,然后取左右孩子大的值
                if (child + 1 < _con.size() && _con[child + 1]>_con[child])
                {
                    child = child + 1;
                }

                if (_com(_con[parent], _con[child]))//类函数
                {
                    swap(_con[parent], _con[child]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }
成功运行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/3170.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT/C++调试技巧:内存泄漏检测

文章目录内存泄漏方案一方案二&#xff1a;CRT调试定位代码位置方法1方法2其它问题方案三&#xff1a;使用vs诊断工具方案四&#xff1a;使用工具VLD&#xff08;Visio Leak Detector&#xff09;方案五Cppcheck内存泄漏 内存泄漏&#xff1a;指的是在程序里动态申请的内存在使…

STM32学习(八)

STM32串口与电脑USB口通信 特别注意&#xff1a;两个设备之间的TXD和RXD&#xff0c;必须交差连接&#xff0c;方可正常通信 RS-232异步通信协议 启动位&#xff1a;必须占1个位长&#xff0c;必须保持逻辑0电平。有效数据位&#xff1a;可选5、6、7、8、9个位长&#xff0c;L…

【嵌入式烧录/刷写文件】-1-详解Motorola S-record(S19/SREC/mot/SX)格式文件

目录 1 什么是Motorola S-record 2 Motorola S-record的格式 2.1 Motorola S-record的结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminator文本行终…

HTTP/2.x:最新的网页加载技术,快速提高您的SEO排名

2.1 http2概念HTTP/2.0&#xff08;又称HTTP2&#xff09;是HTTP协议的第二个版本。它是对HTTP/1.x的更新&#xff0c;旨在提高网络性能和安全性。HTTP/2.0是由互联网工程任务组&#xff08;IETF&#xff09;标准化的&#xff0c;并于2015年发布。2.2 http2.x与http1.x区别HTTP…

spring5(三):IOC操作Bean管理(基于xml方式)

IOC操作Bean管理&#xff08;基于xml方式&#xff09;前言一、基于 xml 方式创建对象二、基于 xml 方式注入属性1. 使用 set 方法进行属性注入2. 使用有参数构造进行属性注入3. p 名称空间注入简化操作&#xff08;了解&#xff09;三、xml 注入其它类型属性1. 字面量2. 注入属…

【云原生之企业级容器技术 Docker实战一】Docker 介绍

目录一、Docker 介绍1.1 容器历史1.2 Docker 是什么1.3 Docker 和虚拟机&#xff0c;物理主机1.4 Docker 的组成1.5 Namespace1.6 Control groups1.7 容器管理工具1.8 Docker 的优势1.9 Docker 的缺点1.10 容器的相关技术1.10.1 容器规范1.10.2 容器 runtime1.10.3 容器管理工具…

看齐iOS砍掉祖传功能,Android 16G内存也危险了

手机内存发展是真的迅速&#xff0c;12GB 没保持几年现在又朝着 16GB 普及。 相比 iOS 的墓碑机制&#xff0c;Android 后台就主打一个真实&#xff0c;只是可惜 APP 不那么老实。 如果你较早接触 Android 机&#xff0c;各种系统管理、优化 APP 的一键加速、清理应该还历历在…

AD9235芯片手册阅读笔记

特征 单个3 V电源操作&#xff08;2.7 V至3.6 V&#xff09; SNR70 dBc至65 MSPS时的奈奎斯特 SFDR85 dBc至65MSPS时奈奎斯特低功率&#xff1a; 300 mW至65 MSPS差分输入&#xff0c;带500 MHz带宽 片上参考和SHA DNL0.4 LSB 灵活模拟输入&#xff1a;1 V p-p至2 V p-p范围 偏…

Python的加密与解密,你知道几类?

人生苦短&#xff0c;我用python python 安装包资料:点击此处跳转文末名片获取 据记载&#xff0c; 公元前400年&#xff0c; 古希腊人发明了置换密码。 1881年世界上的第一个电话 保密专利出现。 在第二次世界大战期间&#xff0c; 德国军方启用“恩尼格玛”密码机&#xff0…

[数据结构] 用两个队列实现栈详解

文章目录 一、队列实现栈的特点分析 1、1 具体分析 1、2 整体概括 二、队列模拟实现栈代码的实现 2、1 手撕 队列 代码 queue.h queue.c 2、2 用队列模拟实现栈代码 三、总结 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x1f64b;‍♂️ &#x1f440; 专栏&#xff1…

入职第一天就被迫离职,找工作多月已读不回,面试拿不到offer我该怎么办?

大多数情况下&#xff0c;测试员的个人技能成长速度&#xff0c;远远大于公司规模或业务的成长速度。所以&#xff0c;跳槽成为了这个行业里最常见的一个词汇。 前言 前几天&#xff0c;我们一个粉丝跟我说&#xff0c;正常入职一家外包&#xff0c;什么都准备好了&#xff0…

Portainer堪称最优秀的容器化管理平台

一、Portainer是什么&#xff1f; Portainer是一款开源的容器管理平台&#xff0c;支持多种容器技术&#xff0c;如Docker、Kubernetes和Swarm等。它提供了一个易于使用的Web UI界面&#xff0c;可用于管理和监控容器和集群。Portainer旨在使容器管理更加简单和可视化&#xf…

WinForm | C# 界面弹出消息通知栏 (仿Win10系统通知栏)

ApeForms 弹出消息通知栏功能 文章目录ApeForms 弹出消息通知栏功能前言全局API通知栏起始方向通知排列方向通知栏之间的间隔距离无鼠标悬停时的不透明度消息通知窗体的默认大小示例代码文本消息提示栏文本消息提示栏&#xff08;带选项&#xff09;图文消息提示栏图文消息提示…

【Spring-boot源码剥析】| 启动原理之侠客行篇

目录一. 传说篇二. 快速启动原理三. 自动配置原理3.1 准备阶段3.2 配置阶段3.3 运行阶段三. Pefect Ending一. 传说篇 江湖传说&#xff0c;有一个神秘的江湖大侠&#xff0c;他名叫SpringBoot&#xff0c;擅长于开发出快速启动的应用程序。这个侠客的江湖名号传遍了整个江湖&a…

did not find expected key while parsing a block mapping at line 2 column 1的解决方法

问题描述 真的是困扰了好久的一个问题&#xff0c;真的是邪乎了&#xff0c;报的错误实际上是错的 完整报错&#xff1a; Error: YAML Exception reading /path_to_your_blog/_publications/2020-08-21.md: (<unknown>): did not find expected key while parsing a b…

JQuery

概述&#xff1a; JQuery&#xff1a;JavaScript和查询&#xff0c;他是辅助JavaScript开发的js类库。 他的的核心思想就是write less&#xff0c;do moire 实现了很多浏览器兼容问题 JQuery的核心函数 $(参数) 1 参数是函数&#xff1a;$(function(){}) window.onlooad fun…

AI风暴 :文心一言 VS GPT-4

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 文心一言 VS GPT-4 文心一言&#xff1a;知识增强大语言模型百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#…

TryHackMe-Zeno(boot2root)

Zeno 你有和伟大的斯多葛派哲学家芝诺一样的耐心吗&#xff1f;试试吧&#xff01; 端口扫描 循例 nmap Web枚举 进到12340端口 目录扫描 /rms是一个业务站点 在admin登录页面尝试弱口令和注入&#xff0c;也都没有成功 SQLI 在点餐这发现了个get参数id&#xff0c;尝试sql…

八大排序算法之归并排序(递归实现+非递归实现)

目录 一.归并排序的基本思想 归并排序算法思想(排升序为例) 二.两个有序子序列(同一个数组中)的归并(排升序) 两个有序序列归并操作代码: 三.归并排序的递归实现 递归归并排序的实现:(后序遍历递归) 递归函数抽象分析: 四.非递归归并排序的实现 1.非递归归并排序算法…

如何从 Vue CLI 迁移到 Vite

如何从 Vue CLI 迁移到 Vite 十一月11 2021如果你在 2021 年之前一直在使用 Vue 进行开发&#xff0c;那么你选择的构建工具很有可能是 Vue CLI。一段时间以来&#xff0c;它一直是脚手架 Vue.js 项目的事实标准。不过现在&#xff0c;Evan You的下一代构建工具Vite已经引起了很…
最新文章