文章目录

  • 声明与初始化
  • 基础函数
    • 构造函数
    • 容器属性函数
    • 队列专属函数
    • 具体成员函数列表....
  • 代码案例
    • 基础初始化,`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

构造函数

  1. 默认构造函数
priority_queue() : priority_queue(Compare(), Container()) { }
  1. 拷贝优先队列构造函数
priority_queue( const priority_queue& other );
  1. 拷贝其他容器构造函数
template< class InputIt >
priority_queue( InputIt first, InputIt last,const Compare& compare, const Container& cont );

容器属性函数

几乎所有C++容器都带有的常见函数,不多赘述

bool        empty() const;      // 检查是否为空
size_type   size()  const;      // 返回容器内元素数量

队列专属函数

  1. 获取队首元素(对于优先队列即为优先度最高元素)
   const_reference top() const;
  1. 移除队首元素
   void pop();
  1. 元素入列
    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 介绍及源码分析相关推荐

  1. Stable Diffusion 原理介绍与源码分析(一)

    Stable Diffusion 原理介绍与源码分析(一) 文章目录 Stable Diffusion 原理介绍与源码分析(一) 前言(与正文无关,可以忽略) 总览 说明 Stable Diffusi ...

  2. GAT 算法原理介绍与源码分析

    GAT 算法原理介绍与源码分析 文章目录 GAT 算法原理介绍与源码分析 零. 前言 (与正文无关, 请忽略) 广而告之 一. 文章信息 二. 核心观点 三. 核心观点解读 四. 源码分析 4.1 G ...

  3. seastar介绍及源码分析

    目录 1.seastar介绍 1.1.机制简介 1.2.使用方法简介 1.2.1 app_template::run 1.2.2 future promise continuation 2.源码分析 ...

  4. ThreadLocal介绍以及源码分析

    ThreadLocal 线程主变量 前面部分引用其他优秀博客,后面源码自己分析的,如有冒犯请私聊我. 用Java语言开发的同学对 ThreadLocal 应该都不会陌生,这个类的使用场景很多,特别是在 ...

  5. Spring_AOP架构介绍与源码分析(含事务深度分析)

    请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...

  6. Redis 的 Sentinel哨兵介绍与源码分析(1):初始化部分

    http://www.redis.cn/topics/sentinel.html redis-6.0.8 本文是在官方中文文档的基础上进行的源码分析,其中包含完整的原文,并在此基础上,添加源码介绍. ...

  7. ArrayList相关方法介绍及源码分析

    目录 ArrayList简介: ArrayList 相关方法介绍 代码表示 相关方法源码分析 ArrayList简介: java.util.ArrayList 是我们最常用的一个类,ArrayList ...

  8. SDIO_WiFi驱动学习之SDIO架构介绍及源码分析

    一.引言 因为WiFi驱动比较复杂,所以WiFi驱动的博客将多分几篇来写. 本篇博客主要介绍Linux下的SDIO架构及源码分析. 本文部分内容摘抄自网络,若有侵权,请联系删除. 二.SDIO WiF ...

  9. python Django之 DRF(一)框架介绍、源码分析

    文章目录 一.django rest framework 框架的介绍 1.什么是RESTful规范? 2.RESTful API的介绍 二.drf框架源码解读 1.drf框架的使用 2.APIView ...

  10. C++ STL: 超详细 容器 deque 以及 适配器queue 和 stack 源码分析

    文章目录 前言 deque 实现 deque类 _Deque_iterator 类 deque 的元素插入 insert函数 deque如何模拟空间连续 queue 实现 stack 的实现 前言 C ...

最新文章

  1. 清华大作业指导:一人单刷雨课堂需要多少工作量?快手工程师详解如何两周搞定...
  2. pmos管的应用_串联稳压电路3:NMOS型、PMOS型
  3. html弹出框交互,HTML5/SVG模态窗口(对话框)交互动画
  4. 16个美艳时尚的的网站设计作品欣赏
  5. 【Red5流媒体服务器搭建】
  6. MATLAB求解线性规划问题
  7. GlusterFS企业级功能之EC纠删码
  8. python在文本添加超链接_在Markdown中快速插入超链接的Workflow
  9. 基于STM32的多功能MP3设计 毕业设计(论文)文献综述
  10. 安装mysql过程中出现无法找到入口,无法定位程序输入点fesetround于动态链接库
  11. 远程控制计算机显示为什么不能满屏,win7系统连接远程桌面却不能全屏显示的解决方法...
  12. payssion支付
  13. fluentd收集K8S日志并以K8S的container_name作为索引名存入Elasticsearch中
  14. Java-设计模式之单例模式
  15. 俄罗斯联邦储蓄银行将采取措施绕开本国加密货币监管
  16. GEE--LandTrendr
  17. 跟着彭亮一起学人工智能之深度学习--零基础学人工智能
  18. Codeforces 786A Berzerk(博弈论)
  19. 英语广播-this is bbc
  20. 3-1 SPIFFS

热门文章

  1. “猫”和路由器是一个东西吗?
  2. 计算机怎么通过网线共享网络,怎样用一根网线联接两台电脑实现网络共享?
  3. Ubuntu18.04下,QT5移植到ARM板上运行程序发生异常:could not find or load the Qt platform plugin linuxfb原因
  4. noob之MySQL基本查询
  5. AcWing 1123. 铲雪车 题解(欧拉回路)
  6. 简单实用算法——人民币金额大写转换
  7. ttl一会255一会64_什么是TTL 生存时间?Ping TTL的含义
  8. 2021年全国职业院校技能大赛获奖名单(高职组网络系统管理)
  9. was 部署php,was 配置web服务器
  10. elasticsearch操作索引库、RestClient操作索引库2