【STL详解 —— priority_queue的使用与模拟实现】

STL详解 —— priority_queue的使用与模拟实现

  • priority_queue的使用
    • priority_queue的介绍
    • priority_queue的定义方式
    • priority_queue各个接口的使用
  • priority_queue的模拟实现
    • 仿函数
    • priority_queue的模拟实现

priority_queue的使用

priority_queue的介绍

在这里插入图片描述

std::priority_queue 是 C++ 标准库中的容器适配器,它提供了一种基于堆的优先级队列实现。优先级队列是一种特殊的队列,其中的元素按照一定的优先级顺序排列,而不是按照它们被插入的顺序。

std::priority_queue 中,插入元素时会根据元素的值自动进行排序,最大(或最小)的元素总是位于队列的顶部。对顶部元素的访问和弹出操作都是 O(1) 的时间复杂度,而插入操作则是 O(log n) 的时间复杂度。

priority_queue的定义方式

在这里插入图片描述
注意看 std::priority_queue的模板参数,总共三个参数

  1. T (Type):这是最重要的模板参数,它表示存储在优先队列中的元素类型。例如,如果要创建一个存储整数的优先队列,则可以指定 int 作为 T 的类型。

  2. Container:这是一个可选的模板参数,用于指定底层容器的类型,默认情况下是 std::vector。优先队列使用底层容器来存储元素,因此可以通过指定不同的容器类型来影响优先队列的性能和行为。

  3. Compare:这也是一个可选的模板参数,用于指定比较函数的类型,默认情况下是 std::less。比较函数用于确定优先队列中元素的顺序,例如 std::less 表示使用 < 运算符来进行比较,创建一个最大堆;而 std::greater 则表示使用 > 运算符来进行比较,创建一个最小堆。

因为后两个参数给了缺省值,所以在定义的时候可以只传一个参数,但注意,缺省只能从右往左缺,不能从左往右缺。

std::priority_queue<int> pq; // 创建一个存储整数的最大堆std::priority_queue<int, std::deque<int>> pq; // 创建一个存储整数的最大堆,使用双端队列作为底层容器std::priority_queue<int, std::vector<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆std::priority_queue<int, std::deque<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆,使用双端队列作为底层容器,使用 std::greater 进行元素比较

priority_queue各个接口的使用

priority_queue的各个成员函数及其功能如下:

成员函数功能
push插入元素到队尾 (并排序)
pop弹出队头元素 (堆顶元素)
top访问队头元素 (堆顶元素)
size获取队列中有效元素个数
empty判断队列是否为空
swap交换两个队列的内容

下面分别演示一个建最大堆和建最小堆的priority_queue。

#include <iostream>
#include <queue>int main() {// 创建一个最大堆,默认使用 std::less 比较函数std::priority_queue<int> maxHeap;// 插入元素maxHeap.push(3);maxHeap.push(1);maxHeap.push(5);// 访问顶部元素std::cout << "Top of maxHeap: " << maxHeap.top() << std::endl; // 输出: 5// 弹出顶部元素maxHeap.pop();// 创建一个最小堆,使用 std::greater 比较函数std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;// 插入元素minHeap.push(3);minHeap.push(1);minHeap.push(5);// 访问顶部元素std::cout << "Top of minHeap: " << minHeap.top() << std::endl; // 输出: 1return 0;
}

priority_queue的模拟实现

仿函数

首先先了解一下仿函数:

仿函数(Functor) 是 C++ 中的一个概念,它是一种行为类似函数的对象,可以像函数一样被调用。仿函数实际上是一个类对象,重载了函数调用运算符 operator()

因为priority_queue是基于堆实现的,在堆排中我们每次的插入元素都需要进行向上调整,每次pop都需要向下调整,所以在下面的模拟实现中我们使用仿函数来进行改写我们之前写过的向上调整和向下调整。

示例仿函数

#include <iostream>// 定义一个仿函数类
class MyFunctor {
public:// 重载函数调用运算符int operator()(int x, int y) {return x + y;}
};int main() {MyFunctor myFunctor; // 创建一个仿函数对象// 使用仿函数对象进行函数调用int result = myFunctor(3, 4);std::cout << "Result: " << result << std::endl; // 输出: 7return 0;
}

priority_queue的模拟实现

我们在之前写过数据结构|堆及其堆排序,我们之前实现的AdjustUpAdjustDown 分别如下:

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 = (parent - 1) / 2;}else{break;}}
}void AdjustDown(HPDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if ((a[child] < a[child + 1]) && child + 1 < size){child++;}if (a[parent] < a[child]){Swap(&a[child], &a[parent]);parent = child;child = child * 2 + 1;}else{break;}}
}

这里我们因为确定元素的类型是int,所以直接使用了 < >来进行比较大小。
下面我们使用仿函数改写:
因为仿函数是一个类对象,所以我们分别使用lessgreater 来表示不同功能的仿函数类。

template<class T>class 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;}};

由参数可知:使用类模板 class Compare 仿函数比普通函数显得更加灵活,完美的诠释了泛型编程。
后期让想改变排序方式只用更改模板参数即可。

template<class T, class Container = std::vector<T>,class Compare = less<T>>void adjust_up(size_t child){Compare com;int parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(size_t parent){Compare com;size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && com(_con[child], _con[child + 1])){++child;}if (com(_con[parent], _con[child])){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}

示例:

//qq::priority_queue<int,vector<int>,greater<int>> pq;qq::priority_queue<int> pq;		//隐式定义,默认是less,建大堆pq.push(1);pq.push(3);pq.push(2);pq.push(8);pq.push(5);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}//8 5 3 2 1

显示调用,建小堆。

	qq::priority_queue<int,vector<int>,greater<int>> pq;//qq::priority_queue<int> pq;pq.push(1);pq.push(3);pq.push(2);pq.push(8);pq.push(5);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}//1 2 3 5 8

建立的是大堆(大顶堆),那么每次从堆中取出的都是当前堆中的最大值,再进行pop,向下调整。因此,如果你从大堆中依次取出所有元素并打印,那么打印出来的序列将是降序的。
同理 建立的是小堆(小顶堆),那么每次从堆中取出的都是当前堆中的最小值,再进行pop,向下调整。因此,如果你从小堆中依次取出所有元素并打印,那么打印出来的序列将是升序的。

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

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

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

相关文章

Java面试八股文(JVM篇)(❤❤)

Java面试八股文_JVM篇 1、知识点汇总2、知识点详解&#xff1a;3、说说类加载与卸载11、说说Java对象创建过程12、知道类的生命周期吗&#xff1f;14、如何判断对象可以被回收&#xff1f;17、调优命令有哪些&#xff1f;18、常见调优工具有哪些20、你知道哪些JVM性能调优参数&…

[Algorithm][双指针][查找总价格为目标值的两个商品][三数之和][四数之和]详细解读 + 代码实现

目录 1.查找总价格为目标值的两个商品1.题目链接2.算法原理讲解3.代码实现 2.三数之和1.题目链接2.算法原理讲解3.代码实现 3.四数之和1.题目链接2.算法原理讲解3.代码实现 1.查找总价格为目标值的两个商品 1.题目链接 题目链接 2.算法原理讲解 由于本题数据有序&#xff0c…

【前端】1. HTML【万字长文】

HTML 基础 HTML 结构 认识 HTML 标签 HTML 代码是由 “标签” 构成的. 形如: <body>hello</body>标签名 (body) 放到 < > 中大部分标签成对出现. <body> 为开始标签, </body> 为结束标签.少数标签只有开始标签, 称为 “单标签”.开始标签和…

Transformer的Decoder的输入输出都是什么

目录 1 疑问&#xff1a;Transformer的Decoder的输入输出都是什么 2 推理时Transformer的Decoder的输入输出 2.1 推理过程中的Decoder输入输出 2.2 整体右移一位 3 训练时Decoder的输入 参考文献&#xff1a; 1 疑问&#xff1a;Transformer的Decoder的输入输出都是什么 …

【Golang】并发编程之三大问题:原子性、有序性、可见性

目录 一、前言二、概念理解2.1 有序性2.2 原子性后果1&#xff1a;其它线程会读到中间态结果&#xff1a;后果2&#xff1a;修改结果被覆盖 2.3 可见性1&#xff09;store buffer(FIFO)引起的类似store-load乱序现象2&#xff09;store buffer(非FIFO)引起的类似store-store乱序…

Day 15 Linux网络管理

IP解析 IP地址组成&#xff1a;IP地址由4部分数字组成&#xff0c;每部分数字对应于8位二进制数字&#xff0c;各部分之间用小数点分开&#xff0c;这是点分2进制。如果换算为10进制我们称为点分10进制。 每个ip地址由两部分组成网络地址(NetID)和主机地址(HostID).网络地址表…

java创建线程池的方法

简介 线程池是一种用于管理和重用线程的机制&#xff0c;它可以有效地管理线程的创建和销毁&#xff0c;减少线程创建和销毁的开销&#xff0c;并且能够控制并发线程数量&#xff0c;避免资源耗尽和系统过载。Java 提供了java.util.concurrent 包来支持线程池的实现。 1.Threa…

靶向中医是新时代的中医

自古以来&#xff0c;中医以其独特的理论和实践体系为人类健康事业作出了巨大的贡献。然而&#xff0c;在现代医学快速发展的背景下&#xff0c;中医的传承与发展面临新的挑战和机遇。靶向中医&#xff0c;作为一种新型的中医诊疗模式&#xff0c;是我们中医增效计划的理论基础…

大模型驱动的汽车行业群体智能技术白皮书2024(175页)

来源&#xff1a;易慧智能&amp清华大学 随着科技的飞速发展&#xff0c;汽车行业正面临着颠覆性的变革。从传统 的燃油车到电动汽车&#xff0c;从手动驾驶到自动驾驶&#xff0c;从机械座舱、电子座 舱到智能座舱&#xff0c;每一次的技术突破都在推动着汽车行业的进步。…

KDTree索引(K近邻搜索,半径R内近邻搜索)——PCL

K近邻搜索&#xff08;K Nearest Neighbors&#xff09; K近邻搜索是一种基于点数量的搜索方法&#xff0c;它会找到指定点附近最接近的K个邻居点。K近邻搜索中的K值是一个参数&#xff0c;您需要指定要搜索的邻居数量。该方法适用于需要查找固定数量邻居点的情况&#xff0c;…

Python基于深度学习的车辆特征分析系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【AIGC】AIGC在虚拟数字人中的应用:塑造未来互动体验的革新力量

&#x1f680; &#x1f680; &#x1f680;随着科技的快速发展&#xff0c;AIGC已经成为引领未来的重要力量。其中&#xff0c;AIGC在虚拟数字人领域的应用更是引起了广泛关注。虚拟数字人作为一种先进的数字化表达形式&#xff0c;结合了3D建模、动画技术、人工智能等多种先进…