[C++STL] Priority Queue 介绍及源码分析
文章目录
- 声明与初始化
- 基础函数
- 构造函数
- 容器属性函数
- 队列专属函数
- 具体成员函数列表....
- 代码案例
- 基础初始化,`push()`,`pop()`操作
- 自定义比较函数: 小顶堆
- 自定义比较函数: 使用`lambda`
- 底层实现
- 构造函数
- top()
- push(const value_type& Val)
- pop()
- LeetCode 实战
- 总结
- 优点
- 缺点
本文将介绍C++ STL 库queue
头文件中的优先队列priority queue,主要涉及基础函数,其底层实现,以及有关应用。
主要参考文档 https://en.cppreference.com/w/cpp/container/priority_queue
声明与初始化
template<class T,class Container = std::vector<T>,class Compare = std::less<typename Container::value_type>
> class priority_queue;
- 声明输入
T
: 指定该优先队列内存放的元素之类型 - 声明输入
Container
: 指定该优先队列的底层容器- 注:该容器必须为支持
front(), push_back(), pop_back()
的有序容器,原因见后文源码,默认为std::vector
- 注:该容器必须为支持
- 声明输入
Compare
:指定优先度的判断条件- 函数模板
bool Compare(T arg_1, T arg_2);
- 注:当该函数返回
True
时,arg1
将晚于arg2
出队,即arg2
优先度更高。换种说法,在队首的元素与其他所有元素进行compare
的结果都是False
。 - 默认为
std::less
,即return arg1 < arg2;
, 如此将为大顶堆,同理std::greater
为小顶堆。
- 函数模板
基础函数
此处将介绍几个较为常用的,完整列表详见 https://en.cppreference.com/w/cpp/container/priority_queue
构造函数
- 默认构造函数
priority_queue() : priority_queue(Compare(), Container()) { }
- 拷贝优先队列构造函数
priority_queue( const priority_queue& other );
- 拷贝其他容器构造函数
template< class InputIt >
priority_queue( InputIt first, InputIt last,const Compare& compare, const Container& cont );
容器属性函数
几乎所有C++容器都带有的常见函数,不多赘述
bool empty() const; // 检查是否为空
size_type size() const; // 返回容器内元素数量
队列专属函数
- 获取队首元素(对于优先队列即为优先度最高元素)
const_reference top() const;
- 移除队首元素
void pop();
- 元素入列
void push( const value_type& value );
具体成员函数列表…
代码案例
基础初始化,push()
,pop()
操作
#include<queue>
#include<iostream>// Print all element in the queue in order
void printQueue(std::priority_queue<int>& q){while(!q.empty()){std::cout << q.top() << ' ';q.pop();}std::cout << std::endl;
}int main(int argc, char const *argv[])
{// Initialize the queuestd::vector<int> val = {1, 5, 3, -10, 0};std::priority_queue<int> max_top_queue(val.begin(), val.end());
#if 0// Equivalent to:std::priority_queue<int, std::vector<int>, std::less<int>> max_top_queue(val.begin(), val.end());
#endif// Try push() and top()std::cout << max_top_queue.top() << std::endl; // 5max_top_queue.push(100); std::cout << max_top_queue.top() << std::endl; // 100// Try pop()printQueue(max_top_queue); // 100 5 3 1 0 -10return 0;
}
自定义比较函数: 小顶堆
#include<queue>
#include<iostream>
int main(int argc, char const *argv[]){std::vector<int> val = {1, 5, 100, 3, -10, 0};// Use greater as compare functionstd::priority_queue<int, std::vector<int>, std::greater<int>>min_top_queue(val.begin(), val.end());// Print allwhile(!min_top_queue.empty()){std::cout << min_top_queue.top() << ' ';min_top_queue.pop();}std::cout << std::endl; // -10 0 1 3 5 100
}
自定义比较函数: 使用lambda
希望实现:对于上文的val
,获得其index
的排序,排序规则为val[index]
的大小
#include<queue>
#include<iostream>
#include <functional>int main(int argc, char const *argv[]){std::vector<int> val = {1, 5, 100, 3, -10, 0};std::vector<int> ind = {0, 1, 2, 3, 4, 5};// Self-design compare function// Use a priority queue to get the order of val, in indexstd::function<bool (int, int)> cmp = [&val](int v_1, int v_2){return val[v_1] < val[v_2]; };std::priority_queue<int, std::vector<int>, decltype(cmp)> max_ind_queue(ind.begin(), ind.end(), cmp);// Print allwhile(!max_ind_queue.empty()){std::cout << max_ind_queue.top() << ' ';max_ind_queue.pop();}std::cout << std::endl; // 2 1 3 0 5 4
}
底层实现
本节将介绍常用函数的底层实现,其中部分函数(如构造函数,push()
,等)存在使用std::move()
的实现,逻辑类似,在此不多赘述。
priority queue
的底层是由heap实现的(heap
的详细内容将在后续更新),并将容器与比较函数作为protected
的成员变量
protected:_Container c{};_Pr comp{};
构造函数
初始化容器及比较函数成员变量,并直接调用make_heap
使得系统将该容器标记为heap
简单来说,此处的操作 将heap
的首尾标记为容器当前的首尾,heap
将搜寻该范围内的最高优先对象。后续每次对容器的操作如push_heap(),pop_heap()
都需要调用heap
相关函数,将heap
的首尾及最高优先级元素进行更新
priority_queue(const _Pr& _Pred, const _Container& _Cont) : c(_Cont), comp(_Pred) {_STD make_heap(c.begin(), c.end(), comp);
}
其余重载构造函数逻辑类似,只不过改变了初始化容器或比较函数的操作
top()
直接返回容器中的首个元素,该操作的原因是 heap
操作会将最高优先度的元素置于容器的首位
_NODISCARD const_reference top() const noexcept(noexcept(c.front())) /* strengthened */ {return c.front();
}
时间复杂度:O(1)O(1)O(1)
push(const value_type& Val)
将新的元素使用push_back()
加入到容器尾部,并调用push_heap
。该操作扩展当前heap
的尾为当前容器的尾(也就是将新的元素纳入heap
的管理范围),接着将容器的首更新为优先度最大的值
void push(const value_type& _Val) {c.push_back(_Val);_STD push_heap(c.begin(), c.end(), comp);
}// 使用std::move(),直接将传入的右值引用,Val,的内容送入容器且不产生拷贝
// 操作结束后Val将变为non-specified
void push(value_type&& _Val) {c.push_back(_STD move(_Val));_STD push_heap(c.begin(), c.end(), comp);
}
时间复杂度:通常为O(log(n))O(log(n))O(log(n)),主要为push_heap()
(O(log(n))O(log(n))O(log(n)))与push_back()
的开销
pop()
与push
同理,但是操作顺序有所不同,首先调用pop_heap()
将容器的首元素(最高优先度元素)置于容器的末尾,随后将第二优先元素置于首位,并更新heap
的尾至容器的尾之前一位(也就是将原先的top()
元素排除管理)。
如上操作后,原容器的首为第二优先度元素,容器的尾为最高优先度元素,此时调用pop_back()
即可将最高优先度元素删去。
void pop() {_STD pop_heap(c.begin(), c.end(), comp);c.pop_back();
}
时间复杂度:通常为O(log(n))O(log(n))O(log(n)),主要为pop_heap()
(O(log(n))O(log(n))O(log(n)))与pop_back()
的开销
LeetCode 实战
No. 347. 前 K 个高频元素 top-k-frequent-elements
No. 703. 数据流中的第 K 大元素 Kth Largest Element in a Stream
No. 1046. 最后一块石头的重量 Last Stone Weight
其中No.347便暴露了priority_queue
不方便直观遍历的缺点
总结
优点
priority_queue
作为对heap
的封装,在不增加复杂度的情况下免去了许多heap
的繁琐操作,并使使用者着重关注于最高优先度的元素。该特性使得priority_queue
在最值问题中频繁被使用。
缺点
相较于底层容器掌握在程序员手中的heap
,**priority_queue
的底层容器无法从外部访问,这使得单纯遍历priority_queue
变得难以直观实现。
[C++STL] Priority Queue 介绍及源码分析相关推荐
- Stable Diffusion 原理介绍与源码分析(一)
Stable Diffusion 原理介绍与源码分析(一) 文章目录 Stable Diffusion 原理介绍与源码分析(一) 前言(与正文无关,可以忽略) 总览 说明 Stable Diffusi ...
- GAT 算法原理介绍与源码分析
GAT 算法原理介绍与源码分析 文章目录 GAT 算法原理介绍与源码分析 零. 前言 (与正文无关, 请忽略) 广而告之 一. 文章信息 二. 核心观点 三. 核心观点解读 四. 源码分析 4.1 G ...
- seastar介绍及源码分析
目录 1.seastar介绍 1.1.机制简介 1.2.使用方法简介 1.2.1 app_template::run 1.2.2 future promise continuation 2.源码分析 ...
- ThreadLocal介绍以及源码分析
ThreadLocal 线程主变量 前面部分引用其他优秀博客,后面源码自己分析的,如有冒犯请私聊我. 用Java语言开发的同学对 ThreadLocal 应该都不会陌生,这个类的使用场景很多,特别是在 ...
- Spring_AOP架构介绍与源码分析(含事务深度分析)
请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...
- Redis 的 Sentinel哨兵介绍与源码分析(1):初始化部分
http://www.redis.cn/topics/sentinel.html redis-6.0.8 本文是在官方中文文档的基础上进行的源码分析,其中包含完整的原文,并在此基础上,添加源码介绍. ...
- ArrayList相关方法介绍及源码分析
目录 ArrayList简介: ArrayList 相关方法介绍 代码表示 相关方法源码分析 ArrayList简介: java.util.ArrayList 是我们最常用的一个类,ArrayList ...
- SDIO_WiFi驱动学习之SDIO架构介绍及源码分析
一.引言 因为WiFi驱动比较复杂,所以WiFi驱动的博客将多分几篇来写. 本篇博客主要介绍Linux下的SDIO架构及源码分析. 本文部分内容摘抄自网络,若有侵权,请联系删除. 二.SDIO WiF ...
- python Django之 DRF(一)框架介绍、源码分析
文章目录 一.django rest framework 框架的介绍 1.什么是RESTful规范? 2.RESTful API的介绍 二.drf框架源码解读 1.drf框架的使用 2.APIView ...
- C++ STL: 超详细 容器 deque 以及 适配器queue 和 stack 源码分析
文章目录 前言 deque 实现 deque类 _Deque_iterator 类 deque 的元素插入 insert函数 deque如何模拟空间连续 queue 实现 stack 的实现 前言 C ...
最新文章
- 清华大作业指导:一人单刷雨课堂需要多少工作量?快手工程师详解如何两周搞定...
- pmos管的应用_串联稳压电路3:NMOS型、PMOS型
- html弹出框交互,HTML5/SVG模态窗口(对话框)交互动画
- 16个美艳时尚的的网站设计作品欣赏
- 【Red5流媒体服务器搭建】
- MATLAB求解线性规划问题
- GlusterFS企业级功能之EC纠删码
- python在文本添加超链接_在Markdown中快速插入超链接的Workflow
- 基于STM32的多功能MP3设计 毕业设计(论文)文献综述
- 安装mysql过程中出现无法找到入口,无法定位程序输入点fesetround于动态链接库
- 远程控制计算机显示为什么不能满屏,win7系统连接远程桌面却不能全屏显示的解决方法...
- payssion支付
- fluentd收集K8S日志并以K8S的container_name作为索引名存入Elasticsearch中
- Java-设计模式之单例模式
- 俄罗斯联邦储蓄银行将采取措施绕开本国加密货币监管
- GEE--LandTrendr
- 跟着彭亮一起学人工智能之深度学习--零基础学人工智能
- Codeforces 786A Berzerk(博弈论)
- 英语广播-this is bbc
- 3-1 SPIFFS
热门文章
- “猫”和路由器是一个东西吗?
- 计算机怎么通过网线共享网络,怎样用一根网线联接两台电脑实现网络共享?
- Ubuntu18.04下,QT5移植到ARM板上运行程序发生异常:could not find or load the Qt platform plugin linuxfb原因
- noob之MySQL基本查询
- AcWing 1123. 铲雪车 题解(欧拉回路)
- 简单实用算法——人民币金额大写转换
- ttl一会255一会64_什么是TTL 生存时间?Ping TTL的含义
- 2021年全国职业院校技能大赛获奖名单(高职组网络系统管理)
- was 部署php,was 配置web服务器
- elasticsearch操作索引库、RestClient操作索引库2