• 【C++ STL】-- priority_queue底层原理、使用、模拟实现


    目录

    priority_queue的使用:

    priority_queue的默认定义的模板:

    priority_queue的定义并初始化:

    priority_queue的模拟实现:

    堆的排序算法:

    堆的向上调整算法核心:

    堆的向下调整算法核心:

    priortiy_queue接口的模拟实现


    priority_queue的使用:

    priority_queue文档介绍

            priority_queue是一个拥有权值观念的queue,它允许加入新元素、移除旧元素、审视旧元素等功能,由于其是一个queue,所以只允许在底端插入元素,并从顶端取出元素,除此之外别无其它存取元素的途径。
            优先级队列默认使用vector 作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。

    priority_queue的默认定义的模板:

    注意:

    • 默认情况下priority_queue是大堆。
    • 默认情况下priority_queue是以vector作为底层容器

    (大多数情况下都是使用vector作为底层容器即可,我们需要变动的只是存储类型、构造堆结构的形式)

    方式一:规定的存储类型,默认内部构造大堆结构

    1. //int类型:
    2. priority_queue<int> q1;
    3. //double类型
    4. priority_queue<double> q2;

    方式二:默认内部构造小堆结构

    priority_queue<int, vector<int>, greater<int>> q;

    priority_queue的定义并初始化:

    template 
             priority_queue (InputIterator first, InputIterator last,
                             const Compare& comp = Compare(),
                             const Container& ctnr = Container());
    
    1. vector<int> v{ 1,2,3,4,5,6,7,8,9,10 };
    2. //使用大堆结构
    3. priority_queue<int> q1(v.begin(), v.end());
    4. //使用小堆结构
    5. priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
    empty测试容器是否为空(公共成员函数)
    size返回大小(公共成员函数)
    top访问顶部元素(公共成员功能)
    push插入元素(公共成员函数)
    emplace(C++11)构造并插入元素(公共成员函数)
    pop删除顶部元素(公共成员函数)
    swap(C++11)交换内容(公共成员函数)
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int main() {
    6. priority_queue<int> q1;
    7. for (int i = 0; i < 10; i++)
    8. {
    9. q1.push(i);
    10. }
    11. //q1:9 8 5 6 7 1 4 0 3 2
    12. vector<int> v{ 10,20,30 };
    13. priority_queue<int> q2(v.begin(), v.end());
    14. //q2:30 20 10
    15. q2.swap(q1);
    16. cout << q1.size() << endl;//输出:3
    17. cout << q2.size() << endl;//输出:10
    18. //q1:30 20 10
    19. //q2:9 8 5 6 7 1 4 0 3 2
    20. while (!q1.empty()) {
    21. cout << q1.top() << " ";
    22. q1.pop();
    23. }
    24. cout << endl;
    25. //输出:30 20 10
    26. return 0;
    27. }

    priority_queue的模拟实现:

    注意,下标计算父子间的关系:

    • leftchild = parent * 2 + 1
    • rightchild = parent * 2 + 2
    • parent = (child - 1) / 2 (因为只会保留整数部分)

    堆的排序算法:

           此处以小根堆为例;(向上调整算法用于存入数据)。

    堆的向上调整算法核心:

    (父节点与子节点大小的比较,子对父)

    • 如果比父亲小,则交换,然后继续向上比较并调整(最多调整到跟节点就结束了)。
    • 如果比父亲大,则调整结束。

    堆的向下调整算法核心:

    (父节点与子节点大小的比较,父对子)

    • 如果比父亲小,则交换,然后继续向下比较并调整。(最多调整到叶子节点就结束了)
    • 如果比父亲大,则调整结束。

    本人的堆的算法博客:

    priortiy_queue接口的模拟实现

            只要知道了堆的向上排序调整算法和向下排序调整算法后,其实prirotiy_queue的模拟实现几乎已经可以说是完成了。

    成员函数实现方法
    push利用容量适配器本身的push_back(),并结合堆的向上调整算法。
    pop根据堆的排序思维,利用swap(),将第一个元素与最后一个元素交换位子(确保中间元素的堆序不被打乱),然后容量适配器本身的pop_back(),再使用堆的向下调整算法。
    top利用容量适配器本身的front()
    size利用容量适配器本身的size()
    empty利用容量适配器本身的empty()

    难度提升:

            priortiy_queue是可以通过容量适配器实现less与gtreater实现,内部堆排序的结构不同。既然需要less与greater,我们也模拟实现一下。

    template, class Compare = less>

    1. template<class T>
    2. class less { bool operator()(T& e1, T& e2) { return e1 < e2; } };
    3. template<class T>
    4. class greater { bool operator()(T& e1, T& e2) { return e1 > e2; } };
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. namespace qcr{
    6. template<class T>
    7. class less { bool operator()(T& e1, T& e2) { return e1 < e2; } };
    8. template<class T>
    9. class greater { bool operator()(T& e1, T& e2) { return e1 > e2; } };
    10. template<class T, class Container = std::vector, class Compare = less>
    11. class priority_queue {
    12. public:
    13. priority_queue():_con() {}
    14. template<class InputIterator>
    15. priority_queue(InputIterator begin, InputIterator end) : _con(begin, end) {
    16. //数据的输入
    17. //while (begin != end) {
    18. // _con.push_back(*begin);
    19. // ++begin;
    20. //}
    21. //数据的大小堆建立
    22. for (int i = (_con.size() - 1 - 1) >> 1; i >= 0; --i)
    23. Adjust_down(i);
    24. }
    25. void Pop() {
    26. assert(!empty());
    27. swap(_con[0], _con[_con.size() - 1]);
    28. _con.pop_back();
    29. Adjust_down(0);
    30. }
    31. bool empty()const {
    32. return _con.empty();
    33. }
    34. size_t size()const {
    35. return _con.size();
    36. }
    37. void push(T x) {
    38. _con.push_back(x);
    39. Adjust_up(_con.size() - 1);
    40. }
    41. const T& top()const
    42. {
    43. return _con.front();
    44. }
    45. //向下排序
    46. void Adjust_down(size_t parent) {
    47. size_t child = parent * 2 + 1;
    48. while (child < _con.size()) {
    49. if (child + 1 < _con.size() && Compare()(_con[child], _con[child + 1]))
    50. ++child;
    51. if (Compare()(_con[parent], _con[child])) {
    52. //将父节点子节点进行调换
    53. std::swap(_con[parent], _con[child]);
    54. //更新父、子节点的位置
    55. parent = child;
    56. child = parent * 2 + 1;
    57. }
    58. else{
    59. //堆已经建立成功
    60. break;
    61. }
    62. }
    63. }
    64. //向上排序
    65. void Adjust_up(size_t child) {
    66. size_t parent = (child - 1) >> 1;
    67. while (child) {
    68. if (Compare()(_con[parent], _con[child])) {
    69. //将父节点子节点进行调换
    70. std::swap(_con[parent], _con[child]);
    71. //更新父、子节点的位置
    72. child = parent;
    73. parent = (child - 1) >> 1;
    74. }
    75. else {
    76. //堆已经建立成功
    77. break;
    78. }
    79. }
    80. }
    81. Container _con;
    82. };
    83. }
  • 相关阅读:
    Maven-构建生命周期与插件
    转换流(解决代码与文件编码不一致读取乱码的问题)(InputStreamReader,OutputStreamWriter)
    你真的了解Java异常处理机制吗?
    QGIS如何给元素添加属性
    树形结构 一篇文章梳理
    代码随想录算法训练营第四十六天| 完全背包、518.零钱兑换 II 、377.组合总和 Ⅳ
    【VSCode】代码高亮的调整
    SpringBoot 01: JavaConfig + @ImportResource + @PropertyResource
    【C语言】详解栈和队列(定义、销毁、数据的操作)
    《持续交付:发布可靠软件的系统方法》- 读书笔记(三)
  • 原文地址:https://blog.csdn.net/weixin_64609308/article/details/127590452