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

priority_queue模拟

一、什么是priority_queue?

        priority_queue是C++标准库中的一个容器适配器,用于实现优先队列(priority queue)的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的优先级是可以通过传入参数自己调整的,它的底层实现通常使用堆(heap)数据结构。以下是堆结构的学习链接:堆(建堆算法,堆排序)-CSDN博客
主要特点
        元素有序:队列中的元素会根据其优先级进行排序,优先级最高的元素总是位于队列的头部(或称为队首)。
        操作:主要操作包括插入新元素(push)、删除优先级最高的元素(pop)以及访问优先级最高的元素(top,但不删除)。
        默认行为:默认情况下,priority_queue使用最大堆实现,即优先级最高的元素(值最大的元素)存储在根节点。但可以通过指定比较函数来改变元素的排序方式,例如使用std::greater可以实现最小堆,即优先级最低的元素(值最小的元素)存储在根节点。

二、priority_queue如何用?

模板参数

priority_queue的模板定义通常包含三个参数:

  • typename T:元素类型。
  • typename Container = std::vector<T>:底层容器类型,默认为std::vector<T>。虽然std::deque也满足条件,但std::vector因其高效的随机访问性能而更常被用作底层容器。
  • typename Compare = std::less<T>:比较函数类型,用于确定元素的优先级。默认为std::less<T>,表示元素按从大到小的顺序排列;若需按从小到大的顺序排列,可指定为std::greater<T>。

以上是priority_queue的接口函数,该篇文章只来学习和模拟c++11以前的接口。

以下是这些接口的使用:

//使用示例
#include<iostream>
#include<queue>
using namespace std;
//这是自定义优先级的一种格式
template<typename T>
class gt
{
public://如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆bool operator()(T a, T b){return a > b;}
private:
};
int main()
{priority_queue<int> heap1;//int表示储存的类型priority_queue<int, vector<int>> heap2;//这里vector表示使用的底层容器,这里也可以换成deque<int>。//greater为编译器提供的类模板,默认的优先级是大堆,而使用它可以生成小堆。priority_queue<int, vector<int>, greater<int>> heap3;//当然也可以自己设计一个优先级方式传入,该方法常常用于储存自定义类型,而内置类型编译器提供的就够用。priority_queue<int, vector<int>, gt<int>> heap4;//push接口用于存入元素heap4.push(2);heap4.push(7);heap4.push(1);heap4.push(5);cout << heap4.size() << endl;//size用于计算队列中元素的个数while (!heap4.empty())//empty用于判断队列是否为空{cout << heap4.top() << ' ';//top获取队头元素heap4.pop();//pop删除队头元素}return 0;
}

以上输出为:

三、priority_queue模拟实现

1.模板参数

       首先为了区别于库里面的优先级队列,我们可以用命名空间限制它的作用域。通过观察库里面的priority_queue模板参数一共有三个,第一个为储存的元素类型,第二个参数为需要用的底层容器(默认为vector),第三个参数为用来调整优先级的类模板(需要我们写一个默认模板),那么我们可以做以下设计:

#include<iostream>
#include<vector>
using namespace std;
namespace byte//用命名空间限制它的作用域,来区别于库里面的优先级队列
{template<typename T, class Container = vector<T>, class Compare = less<T>>class priority_queue{public://...private://...};
}

 注意:这里vector模板我们可以使用库里面的,但优先级队列模板(less<T>)需要我们自己写。

2.成员变量

        STL的任何模拟,如果在考虑成员变量如何设计而发愁,我们可以去想如何储存对象的数据进而来设计我们的成员变量。这里我们用的是容器Container(即vector<int>)来储存数据的,所以我们的成员变量之一类型一定是Container,其次因为想到不同对象传入的Compare可能不同,具有特殊性,所以我们也可以把Compare(优先级调整的类模板)作为成员变量。如下:

private:Container arr;Compare comp;

3.成员函数

        因为在priority_queue模拟中并不涉及到动态内存开辟和一些特殊理,所以这里的构造函数用编译器默认提供的构造函数就够用了,并不需要我们自己编写。

        在优先级队列中因为涉及到堆的调整所以在push和pop接口的设计中有些复杂,其他接口的设计都非常简单就不在讲解,后面大家直接看源码。

3.1.push

        首先需要把arr看做是一个堆结构,无论arr里面有没有元素,然后直接把元素push_back到arr中,此时该元素所在位置为堆底,而且并不一定是正确的位置,接下来要做的就是对该元素进行向上调整。

父子节点的定义:子节点记为child,父节点记为father。

child=arr.size()-1

father=(child-1)/2(理解原理后可以当做公式记忆,可通过一下链接参考学习)

向下调整的方法我在以下文章中有具体讲解:

堆(建堆算法,堆排序)_初始建堆算法-CSDN博客

		void AdjustUP(){int child = arr.size() - 1, father = (child - 1) / 2;while (child > 0){if (comp(arr[father], arr[child])){std::swap(arr[child], arr[father]);child = father;father = (child - 1) / 2;}else{break;}}}

3.2.pop

        该函数主要功能是pop堆顶元素,但是如果直接pop堆顶元素(下标为0)的话,那么下标为1的会成为堆顶元素,会使原来的父子关系和兄弟关系混乱,要重新调整起来极其复杂,所一需要换一种方案,我们可以把堆顶元素与堆底元素交换然后pop堆底元素,然后对堆进行向下调整。

父子节点的定义:子节点记为child,父节点记为father。

father=0

child=father*2+1(这里father和child的计算公式是可以互推的)

向下调整的方法我在以下文章中有具体讲解:

堆(建堆算法,堆排序)_初始建堆算法-CSDN博客

四、源码

#include<iostream>
#include<vector>
using namespace std;
namespace byte
{template<typename T>class less{public://如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆bool operator()(T a, T b){return a < b;}private:};template<typename T>class greater{public://如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆bool operator()(T a, T b){return a > b;}private:};template<typename T, class Container = vector<T>, class Compare = less<T>>class priority_queue{public:void AdjustUP(){int child = arr.size() - 1, father = (child - 1) / 2;while (child > 0){if (comp(arr[father], arr[child])){std::swap(arr[child], arr[father]);child = father;father = (child - 1) / 2;}else{break;}}}void AdjustDOWN(){int father = 0, child = father * 2 + 1;while (child < arr.size()){if (child + 1 < arr.size() && comp(arr[child], arr[child + 1])){child++;}if (comp(arr[father], arr[child])){std::swap(arr[child], arr[father]);father = child;child = father * 2 + 1;}else{break;}}}void push(T x){arr.push_back(x);AdjustUP();}void pop(){std::swap(arr[0], arr[arr.size() - 1]);arr.pop_back();AdjustDOWN();}T top(){return arr[0];}size_t size(){return arr.size();}bool empty(){return arr.size() == 0;}private:Container arr;Compare comp;};
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【动态规划】区间dp
  • 通过SynchronousQueue方式实现线程间数据传递
  • 算法笔记|Day37动态规划X
  • Websocket笔记
  • Tarjan的脱机最小公共祖先算法详解
  • Linux 数据结构 内核链表 栈
  • 联影一面面经
  • 探索VB与ASP.NET的融合艺术:Web开发的高效实践
  • Centos7目前能下载到的位置
  • HSE软件组件有哪些?如何实现HSE与主机的通信(同步/异步)?如何使用HSE提供的安全服务?
  • mybatis if标签判断字符串是否相等
  • 【图像去噪】论文精读:Spatial-Adaptive Network for Single Image Denoising(SADNet)
  • 大数据智能风控核心:模型
  • 通过 pnpm 安装依赖包会发生什么
  • Matlab simulink建模与仿真 第三章(连续模块库)
  • [Vue CLI 3] 配置解析之 css.extract
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 30秒的PHP代码片段(1)数组 - Array
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • Java应用性能调优
  • k8s 面向应用开发者的基础命令
  • log4j2输出到kafka
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • PhantomJS 安装
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 基于Android乐音识别(2)
  • 坑!为什么View.startAnimation不起作用?
  • 容器服务kubernetes弹性伸缩高级用法
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 使用docker-compose进行多节点部署
  • 使用权重正则化较少模型过拟合
  • 思考 CSS 架构
  • 项目实战-Api的解决方案
  • 原生js练习题---第五课
  • C# - 为值类型重定义相等性
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (备忘)Java Map 遍历
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (七)理解angular中的module和injector,即依赖注入
  • (转) ns2/nam与nam实现相关的文件
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .NET程序员迈向卓越的必由之路
  • .NET大文件上传知识整理
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .Net小白的大学四年,内含面经
  • /etc/fstab和/etc/mtab的区别
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)
  • [100天算法】-x 的平方根(day 61)
  • [100天算法】-二叉树剪枝(day 48)
  • [8] CUDA之向量点乘和矩阵乘法