C++ STL - 优先级队列及其模拟实现

目录

0. 引言

1. priority_queue 介绍 

1.1 构造函数 

1.2 priority_queue 接口函数使用 

1.3 仿函数 

 1.4 题目练习

 2. priority_queue 模拟实现

2.1基本框架:

2.2 默认构造函数

2.3 基本函数

2.4 堆的向上以及向下调整


0. 引言

优先队列 (priority_queue) 是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。优先队列和堆本质是一样的,以数组形式存储的完全二叉树。

1. priority_queue 介绍 

1.1 构造函数 

我们可以看到有两种构造方式,一个是构造一个空对象,另一个是通过迭代器的区间来构造,默认的构造出的是大堆

priority_queue<int> pq1; //直接构造空对象

接下来我们分别以大堆和小堆的方式来构造对象:

	vector<int> v1 = {3,2,7,6,0,4,1,9,8,5};priority_queue<int, vector<int>, less<int>> pq1(v1.begin(), v1.end());//less-大堆while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;priority_queue<int, vector<int>, greater<int>> pq2(v1.begin(), v1.end());//greater-小堆while (!pq2.empty()){cout << pq2.top() << " ";pq2.pop();}cout << endl;priority_queue<int> pq3(v1.begin(), v1.end());//默认大堆while (!pq3.empty()){cout << pq3.top() << " ";pq3.pop();}cout << endl;

因此我们得出: less - 大堆, greater - 小堆。 

1.2 priority_queue 接口函数使用 

接口函数主要包括以下:

函数说明
empty检测优先级队列是否为空,是返回true,否则返回 false
top返回优先级队列中最大(最小元素),即堆顶元
push在优先级队列中插入元素x
pop删除优先级队列中最大(最小)元素,即堆顶元素

1.3 仿函数 

仿函数又名函数对象 function objects 仿函数的主要作用是借助类和运算符重载,做到同一格式兼容所有函数。由于模板将 less 用作大堆,而 greater 用做小堆,是在有点别扭,如果是我们自己实现仿函数的化,肯定会按照习惯来写,less 表示小堆,greater 表示大堆。例如:

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;}
};

 1.4 题目练习

优先级队列适合来进行TOPK 以及 排序问题,因为其底层是和堆一模一样的。现在我们一起来看下面这道题:

这题如果不关心时间复杂度,直接利用 sort 排序将会很简单:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {sort(nums.begin(),nums.end());return nums[nums.size()-k];    }
};

 当我们使用优先级队列时,时间复杂度会更好:

//大堆
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int> pq1(nums.begin(), nums.end());for(int i = 0;i < k-1; i++){pq1.pop();}return pq1.top(); }
};
//小堆
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int,vector<int>, greater<int>> pq1(nums.begin(), nums.begin()+k);for(int i = k ;i < nums.size(); i++){if(nums[i] > pq1.top()){pq1.pop();pq1.push(nums[i]);}}return pq1.top(); }  
};

 2. priority_queue 模拟实现

优先级队列的模拟实现,难点在于堆的向上和向下调整。我们首先展现整体代码,然后再逐一分进行分析。

2.1 整体代码

我们单独创建了一个 PriorityQueue.h 文件。

#pragma once
#include<vector>
#include<iostream>
using namespace std;
namespace LHY
{template<class T, class Container = vector<T>>class priority_queue{public:void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size()&& _con[child] < _con[child + 1]){++child;}if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};

从中我们可以看到私有成员变量为底层容器对象。即 Container _con; class Container = vector<T> 。

2.2 基本函数

基本函数为:

void push(const T& x)
{_con.push_back(x);adjust_up(_con.size() - 1);
}void pop()
{swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);
}const T& top()
{return _con[0];
}size_t size()
{return _con.size();
}bool empty()
{return _con.empty();
}

我们可以清楚的看到,优先级队列的插入和删除,返回大小以及判空直接调用底层容器接口即可。而因为需要满足堆的性质,push 和 pop 则需要向上和向下调整堆。此处我们默认建立的是大堆,当push一个数比其父节点大,则需要交换位置,即向上调整。当 pop 操作则是先首尾交换,再删除尾元素,此时不满足堆条件,顶部元素则需要向下调整。如下图所示:

2.3 模拟实现 

void test_priority_queue()
{priority_queue<int> pq;pq.push(1);pq.push(0);pq.push(5);pq.push(2);pq.push(1);pq.push(7);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

2.4 仿函数切换大小堆 

我们在 1.3 介绍了仿函数及其实现,那么我们能不能应用到上面的代码中呢?答案是当然可以。

代码如下:

namespace LHY
{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 Comapre = less<T>>class priority_queue{public:void adjust_up(int child){Comapre com;int parent = (child - 1) / 2;while (child > 0){//if (_con[parent] < _con[child])if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(int parent){size_t child = parent * 2 + 1;Comapre com;while (child < _con.size()){/*if (child + 1 < _con.size()&& _con[child] < _con[child + 1])*/if (child + 1 < _con.size()&& com(_con[child], _con[child + 1])){++child;}/*if (_con[parent] < _con[child])*/if (com(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};

    template<class T, class Container = vector<T>,class Comapre = less<T>>

   if (com(_con[parent], _con[child]))等等的变化使得我们可以改变大小堆。例如:

2.5 日期类 

假设我们现在的数据不是 vector<int> 而是 日期类对象,会发生什么情况呢?我们先看日期类代码:

class Date{public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}private:int _year;int _month;int _day;};class PDateLess{public:bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}};class PDateGreater{public:bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}};

这时候我们创建数据为Date 的优先级队列(大堆),取堆顶元素(判断是否能对自定义类型进行正确调整)

此时没有问题, 因为在实际比较时,调用的是 Date 自己的比较逻辑。

那么如果是Date* 呢?我们再来看:

我们发现多次运行结果不一样 ,因为此时调用的是指针的比较逻辑(地址是随机的,因此结果也是随机的)。

那么,我们要如何去解放呢?可以专门创建一个为Date* 比较的仿函数。

class PDateLess{public:bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}};class PDateGreater{public:bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}};

这样便没有问题了:

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

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

相关文章

分类预测 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积神经网络-长短期记忆网络融合多头注意力机制多特征分类预测

分类预测 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积神经网络-长短期记忆网络融合多头注意力机制多特征分类预测 目录 分类预测 | Matlab实现CNN-LSTM-Mutilhead-Attention卷积神经网络-长短期记忆网络融合多头注意力机制多特征分类预测分类效果基本介绍模型描述程序设计参…

计算机网络(二)物理层

物理层 一、通信基础1.奈氏准则、香农定理2.编码与调制3.电路交换、报文交换、分组交换 二、 传输介质、设备1.导向性传输介质&#xff1a;1.1双绞线1.2 同轴电缆1.3光纤 2.非导向性传输介质&#xff1a; 一、通信基础 信道带宽&#xff1a;信道能通过的最高频率和最低频率之差…

Python爬虫学习完整版

一、什么是爬虫 网络爬虫&#xff0c;是一种按照一定规则&#xff0c;自动抓取互联网信息的程序或者脚本。由于互联网数据的多样性和资源的有限性&#xff0c;根据用户需求定向抓取相关网页并分析也成为如今主流的爬取策略。 1 爬虫可以做什么 你可以爬取网络上的的图片&#…

鸿蒙雄起!风口就在当下,你如何抉择?

近年来&#xff0c;华为自主研发的鸿蒙操作系统&#xff08;HarmonyOS&#xff09;引起了广泛的关注和讨论。鸿蒙系统不仅标志着华为在软件领域的一次重大突破&#xff0c;也预示着全球智能设备市场格局的潜在变化。本文将深入探讨鸿蒙系统的兴起、其在市场上的表现以及对程序员…

刚刚,百度和苹果宣布联名

百度 Apple 就在刚刚&#xff0c;财联社报道&#xff0c;百度将为苹果今年发布的 iPhone16、Mac 系统和 iOS18 提供 AI 功能。 苹果曾与阿里以及另外一家国产大模型公司进行过洽谈&#xff0c;最后确定由百度提供这项服务&#xff0c;苹果预计采取 API 接口的方式计费。 苹果将…

【AI漏洞】人工而后智能

注&#xff1a;公众号暂时不再使用了 本文主要内容&#xff1a; 1、主题&#xff1a;AI漏洞 2、过程&#xff1a;测试步骤 3、笔者&#xff1a;寄语 &#xff08;重点&#xff1a;本文只做技术研究&#xff0c;请遵守相关法律法规&#xff0c;发现自身单位有漏洞请及时修复&…

C语言指针详解(上)

一.什么是指针 指针是一种类型&#xff0c;用来存储变量的地址的类型 有哪些类型呢 字符指针&#xff1a;char* 整型指针&#xff1a;int* 浮点型指针&#xff1a;float* 双精度浮点型指针&#xff1a;double* 空指针&#xff1a;void* &#xff08;每一个类型的指针&a…

搜维尔科技:【应急演练】【工业仿真】救援模拟演练可视化仿真项目实施

安全救援综合演练系统是一套面向公共安全事故、预案管理、应急救援模拟演练的虚拟仿真解决方案&#xff0c;它为警察、消防以及专门的应急救援保障部门提供一个综合的应急救援培训和仿真演练平台。平台主要通过设计不同的事故模型和特定的灾难场景&#xff0c;定制不同的应急救…

Phoenix伪分布安装

引言 Phoenix是构建在HBase上的一个SQL层&#xff0c;能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表&#xff0c;插入数据和对HBase数据进行查询。Phoenix完全使用Java编写&#xff0c;作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫…

大话设计模式之模板方法模式

模板方法模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个算法的框架&#xff0c;将特定步骤的实现延迟到子类中。模板方法模式通过在父类中定义算法的骨架&#xff0c;而将具体步骤的实现留给子类来完成&#xff0c;从而使子类…

Python学习之-正则表达式

目录 前言&#xff1a;1.re.serach1.1例子&#xff1a; 2.re.match2.1示例1&#xff1a;2.2 示例2&#xff1a; 3.re.findall3.1 示例 4.re.fullmatch4.1 示例1&#xff1a;4.2 示例2: 5.re.split5.1 示例1:5.2 示例2&#xff1a;5.3 示例3&#xff1a; 6.re.sub6.1 示例&#…

都2024年了,还不知道怎么学习网络安全?来看看吧,很难找全的

前言 最近收到不少关注朋友的私信和留言&#xff0c;大多数都是零基础小友入门网络安全&#xff0c;需要相关资源学习。其实看过的铁粉都知道&#xff0c;之前的文里是有过推荐过的。新来的小友可能不太清楚&#xff0c;这里就系统地叙述一遍。 01.简单了解一下网络安全 说白…

阿里云ubuntu服务器搭建可视化界面

连接终端 最好初始化服务器的时候 不要以root权限创建 否则会出错 1更新软件: sudo apt-get update2安装ubuntu desktop : sudo apt-get install ubuntu-desktop3 配置ubuntu desktop并重启: sudo apt-get -f install sudo dpkg-reconfigure ubuntu-desktop sudo reboot4 su…

QT文件读写操作和内容提取

访问IO设备&#xff0c;需要先调用open()来设置正确的OpenMode(例如ReadOnly或ReadWrite) 打开设备后后&#xff0c;使用write() 或putChar() 写入数据到文件和设备&#xff0c;并通过调用read()&#xff0c;readLine() 或readAll() 进行读取&#xff1b;使用完设备后&#xf…

把本地文件上传到HDFS上操作步骤

因为条件有限&#xff0c;我这里以虚拟机centos为例 实验条件&#xff1a;我在虚拟机上创建了三台节点&#xff0c;部署了hadoop&#xff0c;把笔记本上的数据上传到hdfs中 数据打包上传到虚拟机节点上 采用的是rz命令&#xff0c;可以帮我们上传数据 没有的话可以使用命令安装…

JetBrains全家桶激活,分享 WebStorm 2024 激活的方案

大家好&#xff0c;欢迎来到金榜探云手&#xff01; WebStorm公司简介 JetBrains 是一家专注于开发工具的软件公司&#xff0c;总部位于捷克。他们以提供强大的集成开发环境&#xff08;IDE&#xff09;而闻名&#xff0c;如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…

使用Qt生成图片

Qt之生成png/jpg/bmp格式图片_qt生成图片-CSDN博客 (1)使用QPainter 示例关键代码&#xff1a; QImage image(QSize(this->width(),this->height()),QImage::Format_ARGB32);image.fill("white");QPainter *painter new QPainter(&image);painter->…

PCL点云处理之M估计样本一致性(MSAC)平面拟合(二百三十六)

PCL点云处理之M估计样本一致性(MSAC)平面拟合(二百三十五六) 一、算法介绍二、使用步骤1.代码2.效果一、算法介绍 写论文当然用RANSAC的优化变种算法MSAC啊,RANSAC太土太LOW了哈哈 MSAC算法(M-estimator Sample Consensus)是RANSAC(Random Sample Consensus)的一种…

【计算机网络】启程

&#x1f4dd;本文介绍 本文为计算机网路系列的开始篇&#xff0c;会介绍一下使用的书籍和自己做的思维导图。 &#x1f44b;作者简介&#xff1a;一个正在积极探索的本科生 &#x1f4f1;联系方式&#xff1a;943641266(QQ) &#x1f6aa;Github地址&#xff1a;https://githu…

【蓝桥杯省赛真题34】python积木搭建 中小学青少年组蓝桥杯比赛 算法思维python编程省赛真题解析

python积木搭建 第十三届蓝桥杯青少年组python比赛省赛真题 一、题目要求 &#xff08;注&#xff1a;input&#xff08;&#xff09;输入函数的括号中不允许添加任何信息&#xff09; 1、编程实现 小蓝和小青在玩积木搭建游戏&#xff0c;具体玩法如下: 小蓝报一个数字N&…