【C++】STL详解(八)—— priority_queue的使用及模拟实现仿函数

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:C++学习
🎯长路漫漫浩浩,万事皆有期待

上一篇博客:【C++】STL详解(七)—— stack和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的定义方式

方式一: 使用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的各个成员函数及其功能如下:

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

示例:

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

仿函数

仿函数/函数对象也是类,是一个类对象。类对象可以像函数一样使用。仿函数要重载operator(),我们通过代码来看一看仿函数:

namespace sherry
{template <class T>class less{public:bool operator()(const T& x, const T& y)const{return x < y;}};template <class T>class greater{public:bool operator()(const T& x, const T& y)const{return x > y;}};}
int main()
{sherry::less<int> lessFunc;lessFunc(1, 2);lessFunc.operator()(1, 2);return 0;
}
//lessFunc是一个对象,仿函数对象,可以像函数一样使用

仿函数的作用在于:在C语言中我们通过传入函数指针解决升序降序问题,虽然C++兼容了C,但是C++并没有继续利用函数指针,而是通过仿函数来控制升序和降序,我们以之前写过的排序为例子,通过利用仿函数来实现升序和降序:

template<class T,class Compare>
void BubbleSort(T* a, int n, Compare com)
{for (int j = 0; j < n; j++){int exchange = 0;for (int i = 1; i < n - j; i++){//if (a[i]<a[i-1])if (com(a[i], a[i-1])){swap(a[i - 1], a[i]);exchange = 1;}}if (exchange == 0){break;}}
}
int main()
{sherry::less<int> lessFunc;sherry::greater<int> greaterFunc;lessFunc(1, 2);int a[] = { 2,3,4,5,6,1,2,8 };BubbleSort(a, sizeof(a) / sizeof(int),lessFunc);for (auto e : a){cout << e << " ";}cout << endl;BubbleSort(a, sizeof(a) / sizeof(int), greaterFunc);for (auto e : a){cout << e << " ";}cout << endl;return 0;
}

传值传参问题:这里是直接传值传参Compare com,当然也可以传引用传参const Compare& com,不过要记得加上const进行修饰,因为一般的仿函数比较小,问题不是很大。

priority_queue的模拟实现

priority_queue的底层实际上就是堆结构,实现priority_queue之前,我们先认识两个重要的堆算法。关于堆算法的具体讲解可以看这篇博客:【树与二叉树】二叉树顺序结构实现以及堆的概念及结构

堆的向上调整算法

以大堆为例,堆的向上调整算法就是在大堆的末尾插入一个数据后,经过一系列的调整,使其仍然是一个大堆。

调整的基本思想如下:

1、将目标结点与其父结点进行比较。
2、若目标结点的值比父结点的值大,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整;若目标结点的值比其父结点的值小,则停止向上调整,此时该树已经是大堆了。

堆的向上调整算法代码:

//堆的向上调整(大堆)
void AdjustUp(vector<int>& v, int child)
{int parent = (child - 1) / 2; //通过child计算parent的下标while (child > 0)//调整到根结点的位置截止{if (v[parent] < v[child])//孩子结点的值大于父结点的值{//将父结点与孩子结点交换swap(v[child], v[parent]);//继续向上进行调整child = parent;parent = (child - 1) / 2;}else//已成堆{break;}}
}

堆的向下调整算法

以大堆为例,使用堆的向下调整算法有一个前提,就是待向下调整的结点的左子树和右子树必须都为大堆。

调整的基本思想如下:

1、将目标结点与其较大的子结点进行比较。
2、若目标结点的值比其较大的子结点的值小,则交换目标结点与其较大的子结点的位置,并将原目标结点的较大子结点当作新的目标结点继续进行向下调整;若目标结点的值比其较大子结点的值大,则停止向下调整,此时该树已经是大堆了。

堆的向下调整算法代码:

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

模拟实现

只要知道了堆的向上调整算法和堆的向下调整算法,priority_queue的模拟实现就没什么困难了。

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

priority_queue的模拟实现代码:

1、基础接口

const T& top() const
{return _con[0];
}bool empty() const
{return _con.empty();
}size_t size() const
{return _con.size();
}

2、push与向上调整

优先级队列就是堆结构,插入一个数之后,我们还需要去进行向上调整

void push(const T& x)
{_con.push_back(x);adjust_up(_con.size() - 1);
}

向上调整算法:

void adjust_up(size_t child)
{//仿函数Compare com;size_t 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;}}
}

3、pop与向下调整

void pop()
{swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);
}

向下调整:

void adjust_down(size_t parent)
{Compare com;//仿函数size_t child = parent * 2 + 1;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;}}
}

4、构造函数

这里主要说一下迭代器区间的构造函数:自定义类型会调用自己的迭代器区间构造,所以我们并不需要一个一个push,走个初始化列表即可,同时,数据进去之后我们还要建堆,利用向下调整算法:从倒数第一个非叶子节点,既最后一个节点的父节点开始进行向下调整:

priority_queue()
{}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last):_con(first, last){for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){adjust_down(i);}}

5、总体代码

namespace sherry //防止命名冲突
{//比较方式(使内部结构为大堆)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:priority_queue(){}//迭代器template <class InputIterator>priority_queue(InputIterator first,InputIterator last){while(first!=last){_con.push_back(*first);++first;}for(int i=0(_con.size()-1-1)/2;i>=0;i++){adjust_down(i);}}//堆的向上调整void adjust_up(int child){	//logNint parent = (child - 1) / 2; //通过child计算parent的下标while (child > 0)//调整到根结点的位置截止{if (_comp(_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(int n, int parent){int child = 2 * parent + 1;//logNwhile (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();adjust_down(_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; //比较方式};
}

总结:

今天我们比较详细地完成了priority_queue的使用及模拟实现,了解了一些有关的底层原理。接下来,我们将进行C++模板进阶操作的的学习。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

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

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

相关文章

11.外观模式

外观模式&#xff08;Facade&#xff09;&#xff0c;为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 UML 测试代码 #include <iostream> using namespace std;class SubSystemOne { pu…

Kafka 源码分析——Producer

文章目录 前言Producer 整体流程Producer 初始化Producer 发送流程执行拦截器逻辑获取集群元数据序列化选择分区消息累加进缓存消息发送 Producer缓冲区Producer 参数调优 前言 在 Kafka 中, 把产生消息的一方称为 Producer 即 生产者&#xff0c;它是 Kafka 的核心组件之一&a…

Spring面试题20:Spring怎样开启注解装配?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring怎样开启注解装配? 要在Spring中开启注解装配,需要进行以下几个步骤: 添加必要的依赖:在项目的构建工具(如Maven或Gradle)配置文件中…

WebGL 选中一个表面

目录 选中一个表面 示例程序&#xff08;PickFace.js&#xff09; 代码详解 gl.readPixels()见126行效果 gl.UNSIGNED_BYTE注意点 示例效果 选中一个表面 ​​​​​​​WebGL 选中物体_山楂树の的博客-CSDN博客可以使用同样的方法来选中物体的某一个表面。这一节在Pi…

苹果手表 Series 6 拆解

步骤 1 苹果手表 Series 6 拆解 Series 6&#xff08;右&#xff09;与具有一年历史的姐妹&#xff08;左&#xff09;的外部比较仅显示出细微的差异&#xff0c;但这就是拆卸的目的。我们已经知道这些细节&#xff1a; LTPO OLED Retina 显示屏针对常亮功能进行了优化——这次…

亚马逊 CodeWhisperer 初体验

1、CodeWhisperer 介绍 CodeWhisperer 是亚马逊出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。类似 Cursor 和 Github Copilot 编码工具。 官网&#xff1a;AI 代码生成器 - Amazon CodeWhisperer - AWS 在编写代码时&#xff0c;它会自动根据您现…

【深度学习实验】前馈神经网络(三):自定义两层前馈神经网络(激活函数logistic、线性层算子Linear)

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. 构建数据集 2. 激活函数logistic 3. 线性层算子 Linear 4. 两层的前馈神经网络MLP 5. 模型训练 一、实验介绍 本实验实现了一个简单的两层前馈神经网络 激活函数…

JavaScript(WebAPI)

目录 一.WebAPI 二.DOM 1.选中页面元素 2.事件 三.操作元素 获取修改元素内容 获取/修改表单元素属性 value type 获取/修改样式属性 1.修改内联样式 2.修改元素应用的CSS类名 四.操作节点 1.新增元素 2.删除元素 五.小结 六.案例 1.网页版本的猜数字 2.表白…

Python | 为FastAPI后端服务添加API Key认证(分别基于路径传参和header两种方式且swagger文档友好支持)

文章目录 01 前言02 路径传参方式添加API Key2.1 完整代码2.2 请求示例2.3 swagger文档测试 03 请求头Header方式传入API Key&#xff08;推荐&#xff09;3.1 完整代码3.2 请求示例3.3 swagger文档测试 01 前言 FastAPI&#xff0c;如其名所示&#xff0c;是一个极为高效的框…

CSS3有哪些新特性

CSS3 引入了许多新特性&#xff0c;以增强样式设计和页面布局的能力&#xff0c;提供更多的视觉效果和交互性。以下是一些 CSS3 中的新特性&#xff1a; 圆角边框&#xff08;Border Radius&#xff09;&#xff1a;圆角的边框&#xff0c;而不是传统的方形边框。 <!DOCTY…

Java面试被问了几个简单的问题,却回答的不是很好

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有需要我的支持&#xff0c;请私信或评论留言&#xff01; 前言 前几天参加了…

解决0-1背包问题(方案二):一维dp数组(滚动数组)

往期文章&#xff1a;解决0-1背包问题&#xff08;方案一&#xff09;:二维dp数组_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/133207350?spm1001.2014.3001.5501 >>探索一维dp数组和二维dp数组的…

深入学习 Redis Cluster - 基于 Docker、DockerCompose 搭建 Redis 集群,处理故障、扩容方案

目录 一、基于 Docker、DockerCompose 搭建 Redis 集群 1.1、前言 1.2、编写 shell 脚本 1.3、执行 shell 脚本&#xff0c;创建集群配置文件 1.4、编写 docker-compose.yml 文件 1.5、启动容器 1.6、构建集群 1.7、使用集群 1.8、如果集群中&#xff0c;有节点挂了&am…

【沐风老师】3DMAX翻转折叠动画插件FoldFx使用方法详解

3DMAX翻转折叠动画插件FoldFx使用方法详解 3DMAX翻转折叠动画插件FoldFx&#xff0c;是3dMax运动图形工具&#xff0c;用于创建多边形折叠动画。用户几乎有无限的可能性&#xff0c;因为动画的每个方面都是可控的。 【适用版本】 适用于3dMax版本&#xff1a;2010及更新版本&a…

让Pegasus天马座开发板实现超声波测距

在完成《让Pegasus天马座开发板用上OLED屏》后&#xff0c;我觉得可以把超声波测距功能也在Pegasus天马座开发板上实现。于是在箱子里找到了&#xff0c;Grove - Ultrasonic Ranger 这一超声波测传感器。 官方地址: https://wiki.seeedstudio.com/Grove-Ultrasonic_Ranger 超声…

OCR -- 文本检测

目标检测&#xff1a; 不仅要解决定位问题&#xff0c;还要解决目标分类问题&#xff0c;给定图像或者视频&#xff0c;找出目标的位置&#xff08;box&#xff09;&#xff0c;并给出目标的类别&#xff1b; 文本检测&#xff1a; 给定输入图像或者视频&#xff0c;找出文本的…

SpringBoot之响应处理

文章目录 前言一、返回值处理器ReturnValueHandler流程关于HttpMessageConverters的初始化ReturnValueHandler与MappingJackson2HttpMessageConverter关联 二、内容协商内容协商原理底层源码 三、自定义MessageConverter总结 前言 包括返回值处理器ReturnValueHandler、内容协…

【Vue.js】使用Element搭建登入注册界面axios中GET请求与POST请求跨域问题

一&#xff0c;ElementUI是什么&#xff1f; Element UI 是一个基于 Vue.js 的桌面端组件库&#xff0c;它提供了一套丰富的 UI 组件&#xff0c;用于构建用户界面。Element UI 的目标是提供简洁、易用、美观的组件&#xff0c;同时保持灵活性和可定制性 二&#xff0c;Element…

套接字socket编程的基础知识点

目录 前言&#xff08;必读&#xff09; 网络字节序 网络中的大小端问题 为什么网络字节序采用的是大端而不是小端&#xff1f; 网络字节序与主机字节序之间的转换 字符串IP和整数IP 整数IP存在的意义 字符串IP和整数IP相互转换的方式 inet_addr函数&#xff08;会自…

相机One Shot标定

1 原理说明 原理部分网上其他文章[1][2]也已经说的比较明白了&#xff0c;这里不再赘述。 2 总体流程 参考论文作者开源的Matlab代码[3]和github上的C代码[4]进行说明&#xff08;不得不说还是Matlab代码更优雅&#xff09; 论文方法总体分两部&#xff0c;第一部是在画面中找…