【C++】STL——priority_queue优先级队列

目录

  • 前言
  • priority_queue的使用
    • 简单使用
    • 在OJ中的使用
  • priority_queue的模拟实现
    • 基本功能
    • 仿函数
    • 在这里插入图片描述

请添加图片描述

前言

  上一节我们说了stackqueue这两种容器适配器,而priority_queue(优先级队列)同样也是属于容器适配器,它会优先处理级别较高的值,如数字最大的,其实也就是数据结构中的。即物理结构上是数组逻辑结构上是一颗完全二叉树

priority_queue的使用

简单使用

库里面的priority_queue:cplusplus–priority_queue

在这里插入图片描述
  优先级队列是一种容器适配器,根据某种严格的弱排序标准,使得它的第一个元素总是它所包含的元素中最大的。
  也就类似于堆,其中元素可以随时插入,并且只能检索堆顶元素(优先级队列中位于顶部的元素)。
我们可以先来查看一下priority_queue的类型:
我们使用typeid来查看一个变量的类型。

#include<iostream>
#include<queue>//优先级队列存在于此头文件中
#include<vector>
using namespace std;int main()
{priority_queue<int> pq;cout << typeid(pq).name() << endl;//查看类型return 0;
}

在这里插入图片描述
由图,该模板中由两个逗号隔开,代表的意思分别是:

  • int:所存储的数据类型
  • class std::vector<int,class std::allocator<int> >:底层所使用的容器
  • struct std::less<int>比较方式,默认是less,即小于比较,决定谁的优先级更高。

priority_queue的接口也不多,就以下几个:

函数说明接口说明
empty()检测是否为空
size()返回元素个数
top()返回优先级队列中最大(最小)元素,即堆顶元素
push(x)在优先级队列中插入x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

使用也很简单:

#include<iostream>
#include<queue>
#include<vector>
using namespace std;int main()
{priority_queue<int> pq;//cout << typeid(pq).name() << endl;pq.push(5);pq.push(8);pq.push(3);pq.push(1);pq.push(9);while (!pq.empty())//输出默认大堆,底层按小于号排序{cout << pq.top() << " ";pq.pop();}cout << endl;vector<int> v = { 8,3,6,1,9,4,5 };/*priority_queue<int,vector<int>,greater<int>> pq1;for (auto e : v){pq1.push(e);}*///直接使用迭代器构造priority_queue<int, vector<int>, greater<int>> pq1(v.begin(), v.end());while (!pq1.empty())//输出默认大堆,底层按小于号排序{cout << pq1.top() << " ";pq1.pop();}cout << endl;return 0;
}

结果如下:
在这里插入图片描述
  可以看到,一旦我把默认的less比较方式换成了greater< int >,它就能实现小堆。

由于比较方式是在第三个参数,所以如果需要修改,就要把第二个参数一起写上。这就是为什么中间还要写vector< int >的原因。

在OJ中的使用

Leetcode.数组中第k个大的元素
题目如下:
  给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
在这里插入图片描述
  题目的意思还是比较容易理解的。我们优先能想到的就是将数组中的值进行排序然后去重,但是任意一种排序方式的时间复杂度都超过了 O(n) ,此时我们就能想到用堆,它既能去重又能排序则就可以使用优先级队列来解决。
  将所有数放入优先级队列中,默认是大堆,此时不断去除堆顶元素,将前k-1个删掉,再取堆顶元素即可。
代码如下:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int> pq(nums.begin(), nums.end());for(int i = 1; i < k; i++){pq.pop();}return pq.top();}
};

priority_queue的模拟实现

  priority_queue优先级队列,属于容器适配器的一种,它和stack和queue一样,都不需要自己去实现功能,只需要调用底层容器的功能即可,但是为了符合堆的规则,在插入和删除时还是要用到向上调整向下调整来实现。

  我们先来看一下优先级队列的一个框架,为方便理解,我们先将模板中的第三个参数给去除了:

namespace emm//使用命名空间,防止与库里面的发生冲突
{template<class T, class container = vector<int>>//默认底层容器为vectorclass priority_queue{pubilc:private:container _con;};
}

基本功能

  对于其判空,取大小以及取堆顶元素都可以直接复用底层容器的实现即可。

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

  当我想要插入一个元素,我需要先尾插这一个数据,然后根据堆的规则,进行向上调整

void push(const T& val)
{_con.push_back(val);AdjustUp(size() - 1);
}
void AdjustUp(int child)//大堆
{int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child])//遵循库里面,小于是大堆//如果孩子节点大于父亲节点,则交换{swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}

可以通过测试来看看:
在这里插入图片描述
运行结果可以看出它是一个大堆。


  当我想pop即删除堆顶元素时,就要用到``向下调整法,我们不能直接将堆顶元素删除,那样会破坏堆的规则,所以要先将堆顶元素最后一个元素进行交换然后将其删除,最后利用向下调整为大堆。
代码如下:

void pop()
{swap(_con[0], _con[_con.size() - 1]);//先交换_con.pop_back();//删除最后一个(堆顶)元素AdjustDown(0);//从堆顶开始向下调整
}
void AdjustDown(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])//遵循库里面,小于是大堆{swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}

这样输出就是排好序的数组了:
在这里插入图片描述


此时我们将构造函数写上:

//强制生成默认构造
priority_queue() = default;template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)//迭代器区间构造
{while (first != last){push(*first);first++;}
}

仿函数

  当我们按照上面正常写法,想把建大堆改成建小堆时,要把向上调整和向下调整中的小于号都改为大于号,这样操作较为繁琐,那么有没有简单一点的操作呢,答案是有的,也就是仿函数。顾名思义,就是对象可以像函数一样调用
例如:
在这里插入图片描述

当然,括号里面带有参数也是可以的。

此时,我们就可以写出两个比较大小的仿函数:

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

这样就可以使得比较大小可以像函数一样去使用
在这里插入图片描述
此时可以将priority_queue中的模板参数变为3个,而参数3的缺省值就是less:

template<class T, class container = vector<int>,class Compare = less<int>>

此时向上调整和向下调整就变为了:

void AdjustUp(int child)//大堆
{Compare com;//实例化一个对象int 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;}elsebreak;}
}void AdjustDown(int parent)
{Compare com;//实例化一个对象int 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[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}

如果我们想要切换成小堆,就按正常操作,写模版实例化时将参数变为greater< int >即可。

vector<int> v = { 8,3,6,1,9,4,5 };
emm::priority_queue<int> pq2(v.begin(), v.end());
while (!pq2.empty())
{cout << pq2.top() << " ";pq2.pop();
}
cout << endl;emm::priority_queue<int, vector<int>, greater<int>> pq3(v.begin(), v.end());
while (!pq3.empty())//变为小堆,按大于号排序
{cout << pq3.top() << " ";pq3.pop();
}
cout << endl;

在这里插入图片描述

感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。
请添加图片描述

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

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

相关文章

【Fargo】9:模拟图片采集的内存泄漏std::bad_alloc

std::bad_alloc 崩溃。这样的内存分配会导致内存耗尽 is simulating an image of size 640x480 with 3 bytes per pixel, resulting in an allocation of approximately 921,600 bytes (or around 900 KB) for each image. The error you’re encountering (std::bad_alloc) ty…

Java 多线程(四)—— 线程安全 与 volatile 与 单例模式

什么是线程安全 在进行多线程编程的时候&#xff0c;当我们编写出来的多线程的代码运行结果不符合我们的预期的时候&#xff0c;这时候就是 bug&#xff0c;这种 bug 是由于多线程的问题而产生出来的 bug 我们称之为 线程安全问题 当我们编写出来的多线程代码运行之后的结果符…

学习文档(5)

Redis应用 目录 Redis应用 Redis 除了做缓存&#xff0c;还能做什么&#xff1f; Redis 可以做消息队列么&#xff1f; Redis 可以做搜索引擎么&#xff1f; 如何基于 Redis 实现延时任务&#xff1f; Redis 除了做缓存&#xff0c;还能做什么&#xff1f; 分布式锁&…

三周精通FastAPI:5 查询参数和字符串校验

FastAPI手册&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/query-params-str-validations/ 查询参数和字符串校验 FastAPI 允许你为参数声明额外的信息和校验。让我们以下面的应用程序为例&#xff1a; from fastapi import FastAPIapp FastAPI()app.get("/it…

基于springboot+thymeleaf+springsecurity搭建一套web小案例

一、前言 本案例中的源代码已上传到资源库&#xff0c;可自行下载&#xff0c;传送阵 https://download.csdn.net/download/qq_36260963/89906196 Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的&#xff0c;使用它可以做到专注于Spring应用的开发&#x…

git clone 鉴权失败

git clone 鉴权失败问题 1. 问题描述2. 解决方法 1. 问题描述 使用git clone自己的代码报如下错误&#xff1a; 正克隆到 xxx... Username for https://github.com: Password for https://xxxgithub.com: remote: Support for password authentication was removed on Augu…

RAG流程的实现与改进

一、 RAG流程图 数据入库&#xff1a;读取本地数据并切成小块&#xff0c;并把这些小块经过编码embedding后&#xff0c;存储在一个向量数据库中&#xff08;下图1——6步&#xff09;&#xff1b;相关性检索&#xff1a;用户提出问题&#xff0c;问题经过编码&#xff0c;再在…

Vue项目中实现拖拽上传附件:原生JS与Element UI组件方法对比

在现代化的Web应用中&#xff0c;文件上传是一个基本功能。随着技术的发展&#xff0c;拖拽上传已经成为提升用户体验的一个重要特性。在Vue项目中&#xff0c;我们可以通过原生JavaScript或使用Element UI组件来实现这一功能。下面我们将分别介绍这两种方法&#xff0c;并对比…

吴恩达深度学习笔记(6)

正交化 为了提高算法准确率&#xff0c;我们想到的方法 收集更多的训练数据增强样本多样性使用梯度下降将算法使算法训练时间更长换一种优化算法更复杂或者更简单的神经网络利用dropout 或者L2正则化改变网络框架更换激活函数改变隐藏单元个数 为了使有监督机制的学习系统良…

vue使用jquery的ajax,页面跳转

一、引入jquery依赖 打开终端更新npm npm install -g npm 更新完后引入输入npm install jquery 加载完后 在最外层的package.json文件中加入以下代码 配置好后导入jquery 设置变量用于接收服务器传输的数据 定义ajax申请数据 服务器的Controller层传输数据 &#xff08;…

【VUE小型网站开发】初始环境搭建

1. 初始化VUE项目 1.1 创建vue项目 1.2 删除多余的界面 根据自己情况删除红框内的文件 清理app页面代码 1.3 引入vue-router 1.3.1 下载vue-router npm install vue-router1.3.2 配置vue-router 在 main.js 或 main.ts 中引入 vue-router import ./assets/main.css im…

Android 图片相识度比较(pHash)

概述 在 Android 中&#xff0c;要比对两张 Bitmap 图片的相似度&#xff0c;常见的方法有基于像素差异、直方图比较、或者使用一些更高级的算法如 SSIM&#xff08;结构相似性&#xff09;和感知哈希&#xff08;pHash&#xff09;。 1. 基于像素的差异比较 可以逐像素比较…

基于MATLAB车牌识别系统设计

MATLAB车牌识别系统设计 实践目的 车牌是一辆汽车独一无二的信息&#xff0c;因此&#xff0c;对车辆牌照的识别技术可以作为 辨识一辆车最为有效的方法。随着ITS(智能交通系统)的高速发展&#xff0c;对车牌识别技术的研究也随之发展。从根本上讲&#xff0c;牌照识别应用了…

中缀表达式转后缀表达式(逆波兰表达式)及如何计算后缀表达式

目录 中缀、后缀表达式简介 中缀转后缀的规则 模拟中缀转后缀 中缀转后缀代码 后缀表达式求值 后缀表达式求值代码 Leetcode相关题目 中缀、后缀表达式简介 首先说说什么是中缀表达式&#xff0c;中缀表达式中&#xff0c;操作符是以中缀形式处于操作数的中间。例如&…

Linux安装Anaconda和Pytorch

又到了一年一度换环境、换服务器不断折腾的时节了&#xff0c;一通折腾后&#xff0c;重新启动遂做记录。 1. Linux安装Anaconda 1.1 离线安装模式 进入官网https://www.anaconda.com/download/success&#xff0c;如图所示&#xff1a; 选择版本进行下载即可。 1.2 在线w…

[Linux网络编程]03-TCP协议

一.TCP协议数据通信的过程 TCP数据报如下&#xff0c;数据报中的标志位双端通信的关键。 三次握手: 1.客户端向服务端发送SYN标志位&#xff0c;请求建立连接&#xff0c;同时发送空包 2.服务端向客户端回发ACK标志位(即确认标志位&#xff0c;任何一端发送数据后都需要另一端…

【VUE】【IOS】【APP】IOS Music APP播放器开发

前言 周末闲来无事&#xff0c;学习了下移动端的一些知识。了解到移动端的一些实现方式&#xff0c;先从最简单的开始。本人没有IOS swift 和Android的开发经验。抱着学习态度从简单的入手&#xff0c;经过了解&#xff0c;本人之前自己用vue的写着玩了几个小项目。看到可以用…

《使用Gin框架构建分布式应用》阅读笔记:p101-p107

《用Gin框架构建分布式应用》学习第7天&#xff0c;p101-p107总结&#xff0c;总计7页。 一、技术总结 1.StatusBadRequest vs StatusInternalServerError 写代码的时候有一个问题&#xff0c;什么时候使用 StatusBadRequest(400错误)&#xff0c;什么时候使用 StatusIntern…

1.2电子商务安全内涵

目录 1 电子商务安全的层次 2 计算机网络安全 3电子商务安全的特点 只有在你生命美丽的时候&#xff0c;世界才是美丽的。 —— 顾城 《顾城哲思录》 1 电子商务安全的层次 安全:主体没有危险的客观状态 电子商务安全是一个广泛的概念&#xff0c;它涉及到电子商务的各个方…

现今 CSS3 最强二维布局系统 Grid 网格布局

深入学习 CSS3 目前最强大的布局系统 Grid 网格布局 Grid 网格布局的基本认识 Grid 网格布局: Grid 布局是一个基于网格的二位布局系统&#xff0c;是目前 CSS 最强的布局系统&#xff0c;它可以同时对列和行进行处理&#xff08;它将网页划分成一个个网格&#xff0c;可以任…