当前位置: 首页 > news >正文

STL C++详解——priority_queue的使用和模拟实现 堆的使用

priority_queue的使用

std::priority_queue 是 C++ 标准模板库(STL)中的一个容器适配器,提供了优先队列的功能。

优先队列:是一种特殊的队列,队列中的每个元素都有与之关联的优先级,优先级高的元素会先出队,而不是像普通队列那样遵循先进先出(FIFO)原则。

使用场景:在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此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是大堆

priority_queue<int> q;

注意⚠️:当我们调用less时是大堆,greater是小堆。(并不知道祖师爷是怎么想的)

功能接口描述复杂度示例代码
插入元素push(const T& value)向优先队列插入一个元素,插入后自动调整维持堆性质O(logn)std::priority_queue<int> pq; pq.push(5);
获取堆顶元素top()返回优先队列的队首元素(大顶堆为最大值,小顶堆为最小值)O(1)std::priority_queue<int> pq; pq.push(5); int top = pq.top();
移除堆顶元素pop()移除优先队列的队首元素,移除后重新调整堆结构O(logn)std::priority_queue<int> pq; pq.push(5); pq.pop();
获取元素数量size()返回优先队列中元素的数量O(1)std::priority_queue<int> pq; pq.push(5); size_t s = pq.size();
检查是否为空empty()检查优先队列是否为空,空则返回 true,否则返回 falseO(1)std::priority_queue<int> pq; bool isEmpty = pq.empty();
#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之前,我们先认识两个重要的堆算法。 

注意⚠️:我以less<T>为例,在STL中是大堆,我是以大堆实现的

堆的性质 

堆具有以下性质

堆中某个结点的值总是不⼤于或不⼩于其⽗结点的值;

堆总是⼀棵完全⼆叉树。

⼆叉树性质

对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从

0 开始编号,则对于序号为 i 的结点有:

  1. i>0 i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,⽆双亲结点
  2.  2i+1<n ,左孩⼦序号: 2i+1 2i+1>=n 否则⽆左孩⼦
  3. 2i+2<n ,右孩⼦序号: 2i+2 2i+2>=n 否则⽆右孩⼦

向上调整算法

向上调整算法

先将元素插⼊到堆的末尾,即最后⼀个孩⼦之后

插⼊之后如果堆的性质遭到破坏,将新插⼊结点顺着其双双亲往上调整到合适位置即可

 

void AdjustUp(int child)
{less<T> com;int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[child], _con[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}

堆的向下调整算法

堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后⼀个数据⼀换,然后删除数组最后⼀个数据,再进⾏

向下调整算法。

 向下调整算法有⼀个前提:左右⼦树必须是⼀个堆,才能调整。

向下调整算法

将堆顶元素与堆中最后⼀个元素进⾏交换

删除堆中最后⼀个元素

将堆顶元素向下调整到满⾜堆特性为⽌

 

 

void AdjustDown(int parent)
{less<T> com;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;}}
}

priority_queue的模拟实现

成员函数实现方法
push在容器尾部插入元素后进行一次向上调整算法
pop将容器头部和尾部元素交换,再将尾部元素删除,最后从根结点开始进行一次向下调整算法
top返回容器的第0个元素
size返回容器的当前大小
empty判断容器是否为空

 

#pragma once
#include<vector>//优先队列默认使用 vector 作为底层容器
namespace wlw
{// 定义一个仿函数 less,用于实现小于比较// 仿函数是一个重载了函数调用运算符(),可像函数一样使用template <class T>struct less{bool operator() (const T& x, const T& y) const{return x < y;}};// 定义一个仿函数 greater,用于实现大于比较template <class T>struct greater{bool operator() (const T& x, const T& y) const{return x > y;}};// 定义一个模板类 priority_queue,实现优先队列的功能// T 表示存储的元素类型// Container 表示底层容器类型,默认使用 vector<T>// Compare 表示比较器类型,默认使用 less<T>,即大顶堆template<class T, class Container = std::vector<T>, class Compare = less<T>>class priority_queue{public:// 使用 default 关键字强制编译器生成默认构造函数priority_queue() = default;//因为下面使用了初始化列表编译器不能默认生成template <class InputIterator>// InputIterator 是一个模板参数,表示输入迭代器类型priority_queue(InputIterator first, InputIterator last):_con(first, last) {// 建堆操作,从最后一个非叶子节点开始,依次进行向下调整// 最后一个非叶子节点的索引为 (size - 1 - 1) / 2 其中size-1为最后一个索引位置for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}}// 向上调整void AdjustUp(int child){// 创建一个比较器对象Compare com;// 计算父节点的索引int parent = (child - 1) / 2;// 当子节点索引大于 0 时,继续调整while (child > 0){// 使用比较器对象比较父节点和子节点if (com(_con[parent], _con[child])){// 如果父节点小于子节点(根据比较器规则),则交换它们std::swap(_con[child], _con[parent]);// 更新子节点和父节点的索引child = parent;parent = (parent - 1) / 2;}else{// 如果父节点大于等于子节点,说明堆的性质已经满足,退出循环break;}}}// 插入元素到优先队列中void push(const T& x){// 将元素添加到底层容器的末尾_con.push_back(x);// 调用 AdjustUp 函数进行向上调整,维护堆的性质AdjustUp(_con.size() - 1);}// 向下调整函数,用于在删除堆顶元素后维护堆的性质void AdjustDown(int 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();// 调用 AdjustDown 函数进行向下调整,维护堆的性质AdjustDown(0);}// 判断优先队列是否为空bool empty(){// 调用底层容器的 empty 函数进行判断return _con.empty();}// 获取堆顶元素的引用const T& top(){// 返回底层容器的第一个元素return _con[0];}// 获取优先队列中元素的数量size_t size(){// 调用底层容器的 size 函数获取元素数量return _con.size();}private:// 底层容器,用于存储优先队列的元素Container _con;};
}

相关文章:

  • jenkins pipeline ssh协议报错处理
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(4):MCP工具开发基础
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(3):MCP资源开发基础
  • JavaScript ?? 运算符详解
  • 宏碁笔记本电脑怎样开启/关闭触摸板
  • 最新项目笔记
  • Qt Creator 创建 Qt Quick Application一些问题
  • C++:STL模板
  • 华为网路设备学习-19 路由策略
  • RS232转Profinet网关开启光谱仪新视界
  • 【Spring Boot】MyBatis多表查询的操作:注解和XML实现SQL语句
  • 1.HTTP协议与RESTful设计
  • 智能指针之设计模式3
  • 如何使用 Spring Boot 实现统一功能处理:从零开始打造高效、可扩展的后台系统
  • 31Calico网络插件的简单使用
  • 常用python爬虫框架介绍
  • 测试第四课---------性能测试工具
  • gbase8s触发器使用
  • 使用 LangChain + Higress + Elasticsearch 构建 RAG 应用
  • Python 获取淘宝买家订单列表(buyer_order_list)接口的详细指南
  • 晋城一男子实名举报村支书打伤其67岁父亲,镇政府:案件正在侦办中
  • 澳大利亚联邦选举投票正式开始
  • 澎湃读报丨央媒头版头条集中刊发:大国应有的样子
  • 美国防部监察机构扩大“群聊门”事件调查范围
  • “非思”的思想——探索失语者的思想史
  • 国务院食安办:加强五一假期食品生产、销售、餐饮服务环节监管