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

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中文叫优先级队列。优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元…...

PyTorch经典模型

PyTorch 经典模型教程 1. PyTorch 库架构概述 PyTorch 是一个广泛使用的深度学习框架&#xff0c;具有高度的灵活性和动态计算图的特性。它支持自动求导功能&#xff0c;并且拥有强大的 GPU 加速能力&#xff0c;适用于各种神经网络模型的训练与部署。 PyTorch 的核心架构包…...

C++ STL容器(三) —— 迭代器底层剖析

本篇聚焦于STL中的迭代器&#xff0c;同样基于MSVC源码。 文章目录 迭代器模式应用场景实现方式优缺点 UML类图代码解析list 迭代器const 迭代器非 const 迭代器 vector 迭代器const 迭代器非const迭代器 反向迭代器 迭代器失效参考资料 迭代器模式 首先迭代器模式是设计模式中…...

力扣416周赛

举报垃圾信息 题目 3295. 举报垃圾信息 - 力扣&#xff08;LeetCode&#xff09; 思路 直接模拟就好了&#xff0c;这题居然是中等难度 代码 public boolean reportSpam(String[] message, String[] bannedWords) {Map<String,Integer> map new HashMap<>()…...

vue 页面常用图表框架

在 Vue.js 页面中&#xff0c;常见的用于制作图表的框架或库有以下几种&#xff1a; ECharts: 官方网站: EChartsECharts 是一个功能强大、可扩展的图表库&#xff0c;支持多种图表类型&#xff0c;如柱状图、折线图、饼图等。Vue 集成: 可以使用 vue-echarts 插件&#xff0c;…...

spring 注解 - @PostConstruct - 用于初始化工作

PostConstruct 是 Java EE 5 中引入的一个注解&#xff0c;用于标注在方法上&#xff0c;表示该方法应该在依赖注入完成之后执行。这个注解是 javax.annotation 包的一部分&#xff0c;通常用于初始化工作&#xff0c;比如初始化成员变量或者启动一些后台任务。 在 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…...

【网页设计】前言

本专栏主要记录 “网页设计” 这一课程的相关笔记。 参考资料&#xff1a; 黑马程序员&#xff1a;黑马程序员pink老师前端入门教程&#xff0c;零基础必看的h5(html5)css3移动端前端视频教程_哔哩哔哩_bilibili 教材&#xff1a;《Adobe创意大学 Dreamweaver CS6标准教材》《…...

STM32巡回研讨会总结(2024)

前言 本次ST公司可以说是推出了7大方面&#xff0c;几乎可以说是覆盖到了目前生活中的方方面面&#xff0c;下面总结下我的感受。无线类 支持多种调制模式&#xff08;LoRa、(G)FSK、(G)MSK 和 BPSK&#xff09;满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…...

54 螺旋矩阵

解题思路&#xff1a; \qquad 这道题可以直接用模拟解决&#xff0c;顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动&#xff0c;每次碰到“边界”时改变方向&#xff0c;边界是不可到达或已经到达过的地方&#xff0c;会随着指针移动不断收缩。 vector<int>…...

基于STM32与OpenCV的物料搬运机械臂设计流程

一、项目概述 本文提出了一种新型的物流搬运机器人&#xff0c;旨在提高物流行业的物料搬运效率和准确性。该机器人结合了 PID 闭环控制算法与视觉识别技术&#xff0c;能够在复杂的环境中实现自主巡线与物料识别。 项目目标与用途 目标&#xff1a;设计一款能够自动搬运物流…...

[万字长文]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工作原理 自动依赖收集&#xff1a;watchEffect不需要明确指定要观察的响应式数据&#xff0c;它会自动收集回调函数中用到的所有响应式数据作为依赖。即时执行&#xff1a;watchEffect的回调函数会在组件的setup()函数执行时立即执行一次&#xff0c;以便能够立即…...

斐波那契数列

在 Python 3.11 中实现斐波那契数列的常见方式有多种&#xff0c;下面我将展示几种不同的实现方法&#xff0c;包括递归、迭代和使用缓存&#xff08;动态规划&#xff09;来优化递归版本。 1. 递归方式&#xff08;最简单但效率较低&#xff09; def fibonacci_recursive(n)…...

TCP并发服务器的实现

一请求一线程 问题 当客户端数量较多时&#xff0c;使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗&#xff1a; 线程创建和管理开销&#xff1a;每个线程都有其创建和销毁的开销&#xff0c;特别是在高并发环境中&#xff0c;这种开销…...

前端大屏自适应方案

一般后台管理页面&#xff0c;需要自适应的也就是大屏这一个&#xff0c;其他的尺寸我感觉用第三方框架继承好的就挺合适的&#xff0c;当然自适应方案也可以同步到所有页面&#xff0c;但我感觉除了 to c 的项目&#xff0c;不太需要所有页面自适应&#xff0c;毕竟都是查看和…...

16.3 k8s容器cpu内存告警指标与资源request和limit

本节重点介绍 : Guaranteed的pod Qos最高在生产环境中&#xff0c;如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。对于 CPU&#xff0c;如果 pod 中服务使用 CPU 超过设置的limits&#xff0c;pod 不会被 kill 掉但会被限制。如果没有设置 li…...

【计算机网络 - 基础问题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

铰链损失函数

铰链损失函数&#xff08;Hinge Loss&#xff09;主要用于支持向量机&#xff08;SVM&#xff09;中&#xff0c;旨在最大化分类间隔。它的公式为&#xff1a; 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修复记录 左侧侧边栏切换&#xff0c;再次切换侧边栏&#xff0c;右侧未从顶部初始位置展示。地图定位展示&#xff0c;可跳转到设置的对应位置。一个页面多个el-dialog弹出框导致渲染层级出现问题。锚点滚动定位错位问题。动态类名绑定。el-tree树形通过 draggable 属性…...

PHY芯片寄存器设计揭秘:从5位地址到分页扩展的演进史

PHY芯片寄存器设计演进&#xff1a;从5位地址到分页扩展的技术革命 当我们在享受千兆以太网带来的高速数据传输时&#xff0c;很少有人会想到这背后隐藏着一场持续了数十年的寄存器架构演进。PHY芯片作为网络通信的物理层核心&#xff0c;其寄存器设计经历了从简单固定到复杂可…...

别再写死代码了!用MCP Tool模块5分钟搞定AI与数据库的安全对话

别再写死代码了&#xff01;用MCP Tool模块5分钟搞定AI与数据库的安全对话 当AI模型需要与数据库交互时&#xff0c;开发者常面临两难选择&#xff1a;要么直接暴露数据库连接信息&#xff0c;要么编写大量胶水代码。这两种方案都存在明显缺陷——前者带来安全隐患&#xff0c;…...

热键冲突解决:从检测到修复的完整指南

热键冲突解决&#xff1a;从检测到修复的完整指南 【免费下载链接】hotkey-detective A small program for investigating stolen hotkeys under Windows 8 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective 在日常电脑使用中&#xff0c;我们经常会遇到这…...

对比学习演进笔记:从Memory Bank到MoCo的负样本队列设计

1. 对比学习的核心思想与演进背景 对比学习&#xff08;Contrastive Learning&#xff09;作为自监督学习的重要分支&#xff0c;其核心思想可以用一句话概括&#xff1a;让相似样本的特征表示尽可能接近&#xff0c;不相似样本的特征表示尽可能远离。这种思想最早可以追溯到20…...

用VSCode+PlatformIO给ESP32做个简易手表:基于LVGL和1.3寸屏的UI实战

基于LVGL的ESP32智能手表开发实战&#xff1a;从硬件驱动到UI设计全流程 在创客圈里&#xff0c;ESP32凭借其出色的性价比和丰富的功能接口&#xff0c;一直是物联网项目的热门选择。而当我们把目光投向更直观的人机交互领域时&#xff0c;LVGL&#xff08;Light and Versatile…...

操作系统-lazy allocation

只有真正需要使用这些页的时候&#xff0c;才进行物理内存页的实际分配sbrk()在xv6操作系统中,进程的用户内存布局由代码段(text)、数据段(data)、堆区(heap)和栈区(stack)组成。sbrk()主要修改的是堆区的大小,堆在xv6中由低地址向高地址拓展。当程序调用sbrk(n)时,操作系统内核…...

告别OpenAI API费用:手把手教你用本地BGE模型+FAISS搭建LangChain私有知识库

零成本构建企业级知识库&#xff1a;基于BGE与FAISS的私有化LangChain解决方案 在AI应用开发领域&#xff0c;数据隐私和成本控制正成为越来越多开发者的核心考量。当OpenAI等商业API按调用次数收费时&#xff0c;频繁的查询请求可能让个人开发者和小型团队不堪重负。更关键的是…...

GLM-OCR实操手册:Web界面上传PNG/JPG/WEBP三格式兼容性验证与建议

GLM-OCR实操手册&#xff1a;Web界面上传PNG/JPG/WEBP三格式兼容性验证与建议 1. 项目概述与测试背景 GLM-OCR是一个基于先进多模态架构的OCR识别模型&#xff0c;专门为处理复杂文档而设计。它不仅能识别普通文字&#xff0c;还能准确识别表格结构和数学公式&#xff0c;在实…...

Ryujinx:C编写的Nintendo Switch模拟器技术解析与应用指南

Ryujinx&#xff1a;C#编写的Nintendo Switch模拟器技术解析与应用指南 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx Ryujinx是一款用C#编写的实验性Nintendo Switch模拟器&#xff…...

教你把歌曲原声调小的5个技巧!简单又好用 赶紧收藏

在日常生活中&#xff0c;调整歌曲原声调小是非常常见的音频处理需求。比如在剪辑视频时&#xff0c;可能需要降低背景音乐的音量以突出旁白&#xff1b;或者在制作播客时&#xff0c;需要平衡人声与背景音的比例&#xff1b;还有在手机上听音乐时&#xff0c;某些歌曲突然出现…...