C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现
1.priority_queque的介绍
1.priority_queue中文叫优先级队列。优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元 素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特 定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。
容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。
2.priority_queue的基本使用
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。
注意: 默认情况下priority_queue是大堆。
2.1构造函数
优先级队列的构造方式有两种:直接构造一个空对象 和 通过迭代器区间进行构造
#include <iostream>
#include <vector>
#include <queue> //注意:优先级队列包含在 queue 的头文件中using namespace std;int main()
{priority_queue<int> pq; //直接构造一个空对象,默认为大堆cout << typeid(pq).name() << endl; //查看类型return 0;
}
注意默认仿函数为less,这个less决定了数据默认建大堆
通过迭代器区间构造对象
#include <iostream>
#include <vector>
#include <queue> //注意:优先级队列包含在 queue 的头文件中using namespace std;int main()
{vector<char> vc = { 'a','b','c','d','e' };priority_queue<char, deque<char>, greater<char>> pq(vc.begin(), vc.end()); //现在是小堆cout << typeid(pq).name() << endl; //查看类型cout << "==========================" << endl;while (!pq.empty()){//将小堆中的堆顶元素,依次打印cout << pq.top() << " ";pq.pop();}return 0;
}
注意:
将比较方式改为 greater 后,生成的是小堆
并且如果想修改比较方式的话,需要指明模板参数2 底层容器
因为比较方式位于模板参数3,不能跳跃缺省(遵循缺省参数规则)

2.2成员函数

#include <iostream>
#include <vector>
#include <queue> //注意:优先级队列包含在 queue 的头文件中using namespace std;void Print(const priority_queue<int>& pq)
{cout << "是否为空:" << pq.empty() << endl;cout << "堆中的有效元素个数:" << pq.size() << endl;cout << "堆顶元素:" << pq.top() << endl;cout << "=================" << endl;
}int main()
{vector<int> v = { 27,15,19,18,28,34,65,49,25,37 };priority_queue<int> pq(v.begin(), v.end()); //默认生成大堆Print(pq);pq.push(10);pq.push(100);Print(pq);pq.pop();pq.pop();pq.pop();Print(pq);return 0;
}

2.3练习题

思路:
利用数组建立大小为 k 的小堆,将剩余数据与堆顶值比较,如果大于,就入堆
为什么建小堆?因为此时需要的是最大的值,建大堆可能会导致次大的值无法入堆
答案:
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int,vector<int>,greater<int>> q(nums.begin(),nums.begin()+k);for(int i=k;i<nums.size();i++){if(q.top()<nums[i]){q.pop();q.push(nums[i]); }}return q.top();}
};
3.模拟实现优先级队列
3.1构造函数
优先级队列 priority_queue 属于容器适配器的一种,像栈和队列一样,没有迭代器
同时也不需要实现自己的具体功能,调用底层容器的功能就行了
不过因为堆比较特殊,需要具备 向上调整 和向下调整 的能力,确保符合堆的规则
首先是基本框架
namespace bit
{template<class T,class Comtainer=vector<T> >class Priority_Queue{public:Priority_Queue() {}template<class InterIterator>private:Comtainer _con;};
}
接着是迭代器区间的默认构造
这个默认构造的功能是传递一段数据的迭代器区间给优先级队列,然后优先级队列会自动在内部将数据排列成大堆或者小堆
namespace bit
{template<class T,class Comtainer=vector<T> >class Priority_Queue{public:Priority_Queue() {}template<class InterIterator>Priority_Queue(InterIterator first,InterIterator end){while (first != end){_con.push_back(*first);/*将数据推送到容器内部*/first++;}for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){Adjust_Down(i);}}private:Comtainer _con;};
}
3.2成员函数
bool empty()const/*判断是否为空:复用底层结构的判空函数*/{return _con.empty();}const T& top()const/*获取堆顶元素:堆顶元素即第一个元素(二叉树的祖宗)*/{return _con[0];}size_t size()const/*获取优先级队列大小:复用获取大小的函数*/{return _con.size();}
注意: 以上三个函数均为涉及对象内容的改变,因此均使用 const 修饰 this 指针所指向的内容
在插入/删除数据后,需要确保堆能符合要求
大堆:父节点比子节点大
小堆:父节点比子节点小
因此每进行一次数据修改相关操作,都需要检查当前堆结构是否被破坏,这一过程称为调整
插入数据:尾插数据,然后向上调整
void Adjust_Up(int child)
{int parent = child / 2 ;while(child>0)if (_con[parent] < _con[child]){std::swap(_con[parent], _con[child]);parent = child;child = parent-1/2;}elsebreak;
}void push(const T& x){_con.push_back(x);Adjust_Up(_con.size()-1);}
删除数据:将堆顶数据交换至堆底,删除堆底元素,再向下调整堆
这步至关重要,pop的时候将堆顶数据交换至堆底,再通过容器的成员函数pop_back删除堆底的元素,也就是删除掉容器存储的数据中最大或者最小的元素,再从堆顶向下调整数据可以重新再这段数据中筛选出最大或者最小的元素推送到堆顶
void Adjust_Down(int parent){int child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size()&_con[child] <_con[child + 1]){child++;}if (_con[parent] <_con[child]){std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}}void pop()const{std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();Adjust_Down(0);}
测试:

3.3仿函数
仿函数又名函数对象 function obiects
需要调用库#include<functional>
仿函数的主要作用是
借助类和运算符重载,做到同一格式兼容所有函数
这有点像函数指针
相比于函数指针又长又难理解的定义,仿函数的使用可谓是很简单了
下面是两个仿函数,作用是比较大小
template<class T>class Less{public:bool operator()(const T& a, const T& b){return a < b;}};template<class T>class Greater{public:bool operator()(const T& a, const T& b){return a > b;}};
此时 priority_queue 中的模板参数升级为3个,而参数3的缺省值就是Less
template<class T,class Comtainer=vector<T>,class Compare=Greater<T> >
像这样,用仿函数生成的对象,将所有要用到比较大小的代码进行替换
Compare greater;
void Adjust_Down(int parent)
{int child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size()&&greater(_con[child] , _con[child + 1])){child++;}if (greater(_con[parent] , _con[child])){std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}
这样就能实现排序顺序的自由控制


3.4特殊情况
在日期类中
class Date
{
public:Date(int year = 1970, 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 std::ostream& operator<<(std::ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};
此时会随机排序,因为我们传入的是指针 ,地址是随机的所以结果也是随机的

所以这里我们专门为它构造一个仿函数就可以了
//小于
template<class T>
struct LessPDate
{bool operator()(const T& p1, const T& p2){return (*p1) < (*p2);}
};//大于
template<class T>
struct GreaterPDate
{bool operator()(const T& p1, const T& p2){return (*p1) > (*p2);}
};

4.源码
源码链接
相关文章:
C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现
1.priority_queque的介绍 1.priority_queue中文叫优先级队列。优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元…...
PyTorch经典模型
PyTorch 经典模型教程 1. PyTorch 库架构概述 PyTorch 是一个广泛使用的深度学习框架,具有高度的灵活性和动态计算图的特性。它支持自动求导功能,并且拥有强大的 GPU 加速能力,适用于各种神经网络模型的训练与部署。 PyTorch 的核心架构包…...
C++ STL容器(三) —— 迭代器底层剖析
本篇聚焦于STL中的迭代器,同样基于MSVC源码。 文章目录 迭代器模式应用场景实现方式优缺点 UML类图代码解析list 迭代器const 迭代器非 const 迭代器 vector 迭代器const 迭代器非const迭代器 反向迭代器 迭代器失效参考资料 迭代器模式 首先迭代器模式是设计模式中…...
力扣416周赛
举报垃圾信息 题目 3295. 举报垃圾信息 - 力扣(LeetCode) 思路 直接模拟就好了,这题居然是中等难度 代码 public boolean reportSpam(String[] message, String[] bannedWords) {Map<String,Integer> map new HashMap<>()…...
vue 页面常用图表框架
在 Vue.js 页面中,常见的用于制作图表的框架或库有以下几种: ECharts: 官方网站: EChartsECharts 是一个功能强大、可扩展的图表库,支持多种图表类型,如柱状图、折线图、饼图等。Vue 集成: 可以使用 vue-echarts 插件,…...
spring 注解 - @PostConstruct - 用于初始化工作
PostConstruct 是 Java EE 5 中引入的一个注解,用于标注在方法上,表示该方法应该在依赖注入完成之后执行。这个注解是 javax.annotation 包的一部分,通常用于初始化工作,比如初始化成员变量或者启动一些后台任务。 在 Spring 框架…...
多机器学习模型学习
特征处理 import os import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedShuffleSplit from sklearn.impute import SimpleImputer from sklearn.pipeline import FeatureUnion fr…...
【网页设计】前言
本专栏主要记录 “网页设计” 这一课程的相关笔记。 参考资料: 黑马程序员:黑马程序员pink老师前端入门教程,零基础必看的h5(html5)css3移动端前端视频教程_哔哩哔哩_bilibili 教材:《Adobe创意大学 Dreamweaver CS6标准教材》《…...
STM32巡回研讨会总结(2024)
前言 本次ST公司可以说是推出了7大方面,几乎可以说是覆盖到了目前生活中的方方面面,下面总结下我的感受。无线类 支持多种调制模式(LoRa、(G)FSK、(G)MSK 和 BPSK)满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…...
54 螺旋矩阵
解题思路: \qquad 这道题可以直接用模拟解决,顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动,每次碰到“边界”时改变方向,边界是不可到达或已经到达过的地方,会随着指针移动不断收缩。 vector<int>…...
基于STM32与OpenCV的物料搬运机械臂设计流程
一、项目概述 本文提出了一种新型的物流搬运机器人,旨在提高物流行业的物料搬运效率和准确性。该机器人结合了 PID 闭环控制算法与视觉识别技术,能够在复杂的环境中实现自主巡线与物料识别。 项目目标与用途 目标:设计一款能够自动搬运物流…...
[万字长文]stable diffusion代码阅读笔记
stable diffusion代码阅读笔记 获得更好的阅读体验可以转到我的博客y0k1n0的小破站 本文参考的配置文件信息: AutoencoderKL:stable-diffusion\configs\autoencoder\autoencoder_kl_32x32x4.yaml latent-diffusion:stable-diffusion\configs\latent-diffusion\lsun_churches-ld…...
watchEffect工作原理
watchEffect工作原理 自动依赖收集:watchEffect不需要明确指定要观察的响应式数据,它会自动收集回调函数中用到的所有响应式数据作为依赖。即时执行:watchEffect的回调函数会在组件的setup()函数执行时立即执行一次,以便能够立即…...
斐波那契数列
在 Python 3.11 中实现斐波那契数列的常见方式有多种,下面我将展示几种不同的实现方法,包括递归、迭代和使用缓存(动态规划)来优化递归版本。 1. 递归方式(最简单但效率较低) def fibonacci_recursive(n)…...
TCP并发服务器的实现
一请求一线程 问题 当客户端数量较多时,使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗: 线程创建和管理开销:每个线程都有其创建和销毁的开销,特别是在高并发环境中,这种开销…...
前端大屏自适应方案
一般后台管理页面,需要自适应的也就是大屏这一个,其他的尺寸我感觉用第三方框架继承好的就挺合适的,当然自适应方案也可以同步到所有页面,但我感觉除了 to c 的项目,不太需要所有页面自适应,毕竟都是查看和…...
16.3 k8s容器cpu内存告警指标与资源request和limit
本节重点介绍 : Guaranteed的pod Qos最高在生产环境中,如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。对于 CPU,如果 pod 中服务使用 CPU 超过设置的limits,pod 不会被 kill 掉但会被限制。如果没有设置 li…...
【计算机网络 - 基础问题】每日 3 题(二十)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…...
铰链损失函数
铰链损失函数(Hinge Loss)主要用于支持向量机(SVM)中,旨在最大化分类间隔。它的公式为: L ( y , f ( x ) ) max ( 0 , 1 − y ⋅ f ( x ) ) L(y, f(x)) \max(0, 1 - y \cdot f(x)) L(y,f(x))max(0,1−…...
项目实战bug修复
实操bug修复记录 左侧侧边栏切换,再次切换侧边栏,右侧未从顶部初始位置展示。地图定位展示,可跳转到设置的对应位置。一个页面多个el-dialog弹出框导致渲染层级出现问题。锚点滚动定位错位问题。动态类名绑定。el-tree树形通过 draggable 属性…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
