【C++初阶(九)】 priority_queue的使用与模拟实现

本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。

💓博主csdn个人主页:小小unicorn
⏩专栏分类:C++
🚚代码仓库:小小unicorn的代码仓库🚚
🌹🌹🌹关注我带你学习编程知识

C++初阶(九)

  • priority_queue的使用
    • priority_queue的介绍
    • priority_queue的定义方式
    • priority_queue的介绍
    • priority_queue各个接口使用
  • priority_queue的模拟实现
    • 堆的向上调整法
    • 堆的向下调整法
  • 建堆时间复杂度:
    • priority_queue的模拟实现

priority_queue的使用

priority_queue的介绍

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

注意: 默认情况下priority_queue是大堆

priority_queue的定义方式

priority_queue的介绍

方式一: 使用vector作为底层容器,内部构造大堆结构。

priority_queue<int, vector<int>, less<int>> q1;

方式二: 使用vector作为底层容器,内部构造小堆结构。

priority_queue<int, vector<int>, greater<int>> q2;

方式三: 不指定底层容器和内部需要构造的堆结构。

priority_queue<int> q;

注意: 此时默认使用vector作为底层容器,内部默认构造大堆结构

priority_queue各个接口使用

priority_queue的各个成员函数及其功能如下:
在这里插入图片描述
使用示例:

#include <iostream>
#include <functional>
#include <queue>using namespace std;
int main()
{priority_queue<int> q;q.push(3);q.push(6);q.push(0);q.push(2);q.push(9);q.push(8);q.push(1);while (!q.empty()){cout << q.top() << " ";q.pop();}cout << endl; //9 8 6 3 2 1 0return 0;
}

在这里插入图片描述

priority_queue的模拟实现

priority_queue的底层实际上就是堆结构,实现priority_queue之前,我们先认识两个重要的堆算法。(下面这两种算法我们均以大堆为例)

堆的向上调整法

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。
在这里插入图片描述
向上调整算法的基本思想(以建小堆为例):
 1.将目标结点与其父结点比较。
 2.若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,则停止向上调整,此时该树已经是小堆了。

在这里插入图片描述
代码如下:

//交换函数
void Swap(HPDataType* x, HPDataType* y)
{HPDataType tmp = *x;*x = *y;*y = tmp;
}//堆的向上调整(小堆)
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0)//调整到根结点的位置截止{if (a[child] < a[parent])//孩子结点的值小于父结点的值{//将父结点与孩子结点交换Swap(&a[child], &a[parent]);//继续向上进行调整child = parent;parent = (child - 1) / 2;}else//已成堆{break;}}
}

堆的向下调整法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
在这里插入图片描述
但是:向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

1.若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
2.若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

在这里插入图片描述

向下调整算法的基本思想(以建小堆为例):
 1.从根结点处开始,选出左右孩子中值较小的孩子。
 2.让小的孩子与其父亲进行比较。

 若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。

 若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

代码如下:

//交换函数
void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//堆的向下调整(小堆)
void AdjustDown(int* a, int n, int parent)
{//child记录左右孩子中值较小的孩子的下标int child = 2 * parent + 1;//先默认其左孩子的值较小while (child < n){if (child + 1 < n&&a[child + 1] < a[child])//右孩子存在并且右孩子比左孩子还小{child++;//较小的孩子改为右孩子}if (a[child] < a[parent])//左右孩子中较小孩子的值比父结点还小{//将父结点与较小的子结点交换Swap(&a[child], &a[parent]);//继续向下进行调整parent = child;child = 2 * parent + 1;}else//已成堆{break;}}
}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN)

上面说到,使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。
在这里插入图片描述
代码如下:

	//建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(php->a, php->size, i);}

建堆时间复杂度:

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述
利用错位相减法进行计算:
在这里插入图片描述
因此:建堆的时间复杂度为O(N)

总结:
堆的向下调整算法的时间复杂度:T(n)=O(logN)。
建堆的时间复杂度:T(n)=O(N)。

priority_queue的模拟实现

只要知道了堆的向上调整算法和堆的向下调整算法,priority_queue的模拟实现就没什么困难了。
在这里插入图片描述
priority_queue的模拟实现代码:

namespace NIC //防止命名冲突
{//比较方式(使内部结构为大堆)template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};//比较方式(使内部结构为小堆)template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};//优先级队列的模拟实现template<class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{public://堆的向上调整void AdjustUp(int child){int parent = (child - 1) / 2; //通过child计算parent的下标while (child > 0)//调整到根结点的位置截止{if (_comp(_con[parent], _con[child]))//通过所给比较方式确定是否需要交换结点位置{//将父结点与孩子结点交换swap(_con[child], _con[parent]);//继续向上进行调整child = parent;parent = (child - 1) / 2;}else//已成堆{break;}}}//插入元素到队尾(并排序)void push(const T& x){_con.push_back(x);AdjustUp(_con.size() - 1); //将最后一个元素进行一次向上调整}//堆的向下调整void AdjustDown(int n, int parent){int child = 2 * parent + 1;while (child < n){if (child + 1 < n && _comp(_con[child], _con[child + 1])){child++;}if (_comp(_con[parent], _con[child]))//通过所给比较方式确定是否需要交换结点位置{//将父结点与孩子结点交换swap(_con[child], _con[parent]);//继续向下进行调整parent = child;child = 2 * parent + 1;}else//已成堆{break;}}}//弹出队头元素(堆顶元素)void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();AdjustDown(_con.size(), 0); //将第0个元素进行一次向下调整}//访问队头元素(堆顶元素)T& top(){return _con[0];}const T& top() const{return _con[0];}//获取队列中有效元素个数size_t size() const{return _con.size();}//判断队列是否为空bool empty() const{return _con.empty();}private:Container _con; //底层容器Compare _comp; //比较方式};
}

测试一下:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

删除链表的倒数第N个节点,剑指offerII(21),力扣

目录 题目地址&#xff1a; 题目&#xff1a; 相似类型题&#xff1a; 我们直接看本题题解吧&#xff1a; 解题方法&#xff1a; 难度分析&#xff1a; 解题分析&#xff1a; 解题思路&#xff08;双指针&#xff09;&#xff1a; 代码实现&#xff1a; 代码说明&#xff1a; 代…

C++基础 -8- 函数重载

函数重载格式(图片代码段呈现) #include "iostream"using namespace std;void rlxy(int a) {cout << "int a"<< endl; }void rlxy(char a) {cout << "char a"<< endl; }int main() {rlxy(99);rlxy(c); }函数重载的依据…

从Android面试题目溯源-1、创建线程有那几种方式

概念 程序执行流的最小单位&#xff0c;处理器调度调度和分派的基本单位。 如何理解这个概念 如下图&#xff0c;可以简单类比吉他&#xff0c;六根弦代表六个线程&#xff0c;每个线程独立且单独运行&#xff0c;且持有上一个音的状态&#xff0c;每根手指可类比为一个CPU的…

matlab绘图函数plot和fplot的区别

一、背景 有的函数用plot画就会报错&#xff0c;显示数据必须为可转换为双精度值的数值、日期时间、持续时间、分类或数组。 如下图所示&#xff1a; 但用fplot函数就没有问题&#xff0c;因此这里记录一下两者的区别&#xff0c;如果使用不当&#xff0c;画出的图可能就是下…

Linux网络编程 SQLite库(TCP Socket 服务器 客户端)

聊天室系统 SQLite库 服务器 客户端 聊天室系统 当设计一个聊天室系统时&#xff0c;会涉及到许多方面的知识。简单的项目结构&#xff0c;从不同的方面对聊天室的设计进行分析。请注意&#xff0c;以下的结构只是一个示例&#xff0c;实际项目可能会更加复杂&#xff0c;具体…

在CentOS 8.2中安装Percona Xtrabackup 8.0.x备份MySql

添加Percona软件库&#xff1a; yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm 安装Percona Xtrabackup 8.0.x&#xff1a; yum install percona-xtrabackup-80 确认安装完成后&#xff0c;您可以使用以下命令验证Percona Xtrabackup的安装…

23种设计模式之C++实践

23种设计模式之C++实践 1. 简介2. 基础知识3. 设计模式(一)创建型模式1. 单例模式1.2 饿汉式单例模式1.3 懒汉式单例模式比较IoDH单例模式总结2. 简单工厂模式简单工厂模式总结3. 工厂方法模式工厂方法模式总结4. 抽象工厂模式抽象工厂模式总结5. 原型模式原型模式总结6. 建造…

Leetcode算法之哈希表

目录 1.两数之和2.判定是否互为字符重排3.存在重复元素I4.存在重复元素II5.字母异位词分组 1.两数之和 两数之和 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int,int> hash;for(int i0;i<nums.si…

2020年2月25日 Go生态洞察:Go 1.14版本发布

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

linux进入telnet和推出telnet

安装telnet centos7 yum install -y telnet ubuntu apt install -y telnet 进入telnet telnet ip port 退出telnet 1. 按下下面的组合键 ctrl] 2. 输入下面命令推出 quit

ipconfig出来各个字段的意思

1 ipconfig 是一个用于显示当前网络接口配置信息的命令。在 Windows 操作系统下&#xff0c;运行 ipconfig 后显示的信息有以下几个部分&#xff1a; 以太网适配器/无线局域网适配器的名称&#xff1a;这些是您计算机上安装的网络适配器的名称。 物理地址&#xff08;Physica…

电荷泵升压/降压电路

一、升压\降压电路原理分析 1、升压电路 电荷泵升压电路 VoutVa5V 5V_PLUS0V时&#xff0c;Va给C2充电&#xff0c;C2上节点电压比C2下节点电压高Va&#xff1b; 5V_PLUS5V时&#xff0c;C2电压不能突变&#xff0c;C2上节点电压依然比C2下节点电压高Va&#xff0c;但C2下节点…

关于AM5系列微机保护装置在某产业园配电工程中的应用-安科瑞 蒋静

1 摘要 目前&#xff0c;微机保护装置广泛应用于电力系统中&#xff0c;该类装置能够合理监测电力系统的运行状况&#xff0c;并实时记录电力系统出现故障的位置及性质&#xff0c;从而为故障的快速处理提供合理的参考信息。本文介绍的AM5系列微机保护装置&#xff0c;可以针对…

东用科技智能公交识别系统无线传输方案

在科技不断进步和人工智能快速发展的当下&#xff0c;人脸识别技术已逐渐应用于各个领域。其中&#xff0c;公共交通领域便是重要的应用场景之一。人脸识别技术的引入可以提高交通的安全性、效率及便利性。 为了实现公交公司对乘客的身份识别和安全管理的需求&#xff0c;提高运…

【2023.11.26】Mybatis注解学习

注解执行SQL语句 Mybatis的映射接口需要写在映射器xml的命名空间内。为了省略这一步&#xff0c;可以使用注解开发。 Select("select * from artist where aID #{a}")artist getArtistById(int a); 在接口的方法上加上注解&#xff0c;注解中写明SQL语句即可省略映…

CVPR 2023 精选论文学习笔记:UniSim A Neural Closed-Loop Sensor Simulator

基于MECE原则,我们给出以下分类标准: 标准 1:仿真类型 仿真类型是指仿真器是否能够实时生成场景。实时仿真器能够以每秒至少 30 帧的速度生成图像和视频,使其适用于训练和测试自动驾驶汽车等机器人。另一方面,离线仿真器不是实时的,但它们可以生成更逼真的图像和视频。这…

【Linux系统编程】进程概念详解(什么是进程?如何查看进程?)

目录 一、前言 二、 什么是进程&#xff1f; &#x1f4a6;引出进程 &#x1f4a6;进程的基本概念 &#x1f4a6;理解进程 ⭐描述进程--PCB&#xff08;进程控制块&#xff09; ⭐组织进程 三、查看进程 &#x1f4a6; 通过 ps 命令查看进程 &#x1f4a6; 通过 l…

CocosCreator 面试题(十六)Cocos Creator 节点池的基本原理是什么?如何使用?

一、Cocos Creator 节点池的基本原理是什么&#xff1f; Cocos Creator 是一个游戏开发引擎&#xff0c;它提供了节点池&#xff08;Node Pool&#xff09;的功能&#xff0c;用于管理和重用游戏中的节点对象。节点池的基本原理如下&#xff1a; 创建初始节点&#xff1a;在游戏…

leetcode做题笔记828. 统计子串中的唯一字符

我们定义了一个函数 countUniqueChars(s) 来统计字符串 s 中的唯一字符&#xff0c;并返回唯一字符的个数。 例如&#xff1a;s "LEETCODE" &#xff0c;则其中 "L", "T","C","O","D" 都是唯一字符&#xff0c;…

口袋参谋:如何识别买家旺旺号?这招超简单!

​想要不被骗钱、跑路&#xff01;那商家在销量递增之前&#xff0c;一定要验买家旺旺号&#xff01;那如何快速验出买家是人还是“鬼”&#xff0c;我们就需要借助验号工具了。 说到这个验号工具&#xff0c;我不得不说&#xff0c;口袋参谋照妖镜查号功能&#xff0c;一键快速…