• C++入门篇10---stack+queue+priority_queue


    前言

    本文主要是介绍C++库中的栈、队列和优先级队列(其实就是堆)的一些接口以及如何用C++来实现它们,对这三种数据结构就不多介绍了,如有不了解的同学,请查阅我之前写的两篇博客

    下面正片开始

    一、stack

    1.了解stack的相关接口

    函数名称接口说明
    empty()查看栈是否为空
    size()查看栈中的元素个数
    push()将元素压入栈中
    pop()从栈顶删除元素
    top()返回栈顶元素
    stack()构造空栈
    1. void test1()
    2. {
    3. //后进先出
    4. stack<int>st;
    5. st.push(1);
    6. st.push(3);
    7. st.push(4);
    8. st.push(2);
    9. while (st.size())
    10. {
    11. cout << st.top() << endl;
    12. st.pop();
    13. }
    14. }

    2.stack的实现

    1. //这里统一说明一下,栈、队列、优先级队列都是适配器容器,不是容器
    2. //所谓的适配器容器就是对其他容器的接口进行包装,改造实现的
    3. //我们往下看就会发现,stack的实现基本上都是在调用其他容器的函数
    4. //其实我们在学数据结构的时候,就已经发现栈既可以用数组实现,也能用链表实现
    5. //本质就是因为数组和链表两种数据结构的功能都符合stack的需求,这里也是一样
    6. //stack不关心它用的什么容器实现,它只关心该容器是否有它需要的功能函数
    7. //同时这里也体现了C++库的发明人的智慧,所有容器的功能相同的函数名称基本都一样(可谓妙绝,请细品)
    8. namespace zxws {
    9. //deque是双端队列---该容器的底层实现以后有机会再讲,各位可以去看一眼deque的文档,
    10. //它也支持下面的几个函数,同时它的底层实现和stack最搭配,所以库里的stack默认用它实现
    11. template<class T, class Container = deque>
    12. class Stack {
    13. private:
    14. Container _con;
    15. public:
    16. T& top() {
    17. return _con.back();
    18. }
    19. const T& top() const{
    20. return _con.back();
    21. }
    22. void pop() {
    23. _con.pop_back();
    24. }
    25. void push(const T& val) {
    26. _con.push_back(val);
    27. }
    28. size_t size() const{
    29. return _con.size();
    30. }
    31. bool empty() {
    32. return _con.empty();
    33. }
    34. };
    35. }

    二、queue

    1.了解queue的相关接口

    函数声明接口说明
    empty()判断队列是否为空
    size()查看队列中的元素个数
    push()向队列中插入元素
    pop()从队头删除元素
    front()返回队头元素
    back()返回队尾元素
    queue()构造空队列

    1. void test2()
    2. {
    3. queue<int>q;
    4. q.push(1);
    5. q.push(3);
    6. q.push(4);
    7. q.push(2);
    8. while (q.size())
    9. {
    10. cout << q.front() << endl;
    11. q.pop();
    12. }
    13. }

     2.queue的实现

    1. namespace zxws {
    2. template<class T,class Container = deque>
    3. //deque的底层实现和queue最搭配,所以库里的queue默认用它实现
    4. //当然这并不是说deque就比vector、list更好,只是各有各的适用场景
    5. class Queue {
    6. private:
    7. Container _con;
    8. public:
    9. T& front() {
    10. return _con.front();
    11. }
    12. T& back() {
    13. return _con.back();
    14. }
    15. const T& front() const {
    16. return _con.front();
    17. }
    18. const T& back() const {
    19. return _con.back();
    20. }
    21. void pop() {
    22. _con.pop_front();
    23. }
    24. void push(const T& val) {
    25. _con.push_back(val);
    26. }
    27. size_t size() {
    28. return _con.size();
    29. }
    30. bool empty() {
    31. return _con.empty();
    32. }
    33. };
    34. }

    三、priority_queue(优先级队列)---堆(heap)

    1.了解priority_queue的相关接口

    函数声明接口说明
    priority_queue()/priority_queue(first,last)构造一个空的优先级队列/用一段迭代器区间初始化
    empty()查看优先级队列是否为空
    size()返回优先级队列中的元素个数
    push()向优先级队列中插入元素
    top()返回优先级队列中的最大/最小元素,即堆顶元素
    pop()删除优先级队列中的最大/最小元素,即堆顶元素
    1. void test3()
    2. {
    3. priority_queue<int>q;//默认大堆
    4. //priority_queue, greater>q;//小堆
    5. //这里后面的实现中会讲到
    6. q.push(1);
    7. q.push(3);
    8. q.push(4);
    9. q.push(2);
    10. while (q.size())
    11. {
    12. cout << q.top() << endl;
    13. q.pop();
    14. }
    15. }

     2.priority_queue的实现

    1. namespace zxws
    2. {
    3. template <class T, class Container = vector, class Compare = less >
    4. class priority_queue
    5. {
    6. public:
    7. priority_queue(){}
    8. void AdjustDown(int parent)
    9. {
    10. int n = c.size();
    11. for (int child = parent * 2 + 1; child < n; parent = child, child = parent * 2 + 1)
    12. {
    13. if (child + 1 < n && comp(c[child],c[child+1]))
    14. child++;
    15. if (comp(c[parent], c[child]))
    16. swap(c[parent], c[child]);
    17. else
    18. break;
    19. }
    20. }
    21. void AdjustUp(int child)
    22. {
    23. for (int parent = (child - 1) / 2; child > 0; child = parent, parent = (child - 1) / 2)
    24. {
    25. if (comp(c[parent], c[child]))
    26. swap(c[parent], c[child]);
    27. else
    28. break;
    29. }
    30. }
    31. template <class InputIterator>
    32. priority_queue(InputIterator first, InputIterator last)
    33. :c(first,last)
    34. {
    35. for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--)
    36. AdjustDown(i);
    37. }
    38. bool empty() const
    39. {
    40. return c.empty();
    41. }
    42. size_t size() const
    43. {
    44. return c.size();
    45. }
    46. const T& top() const
    47. {
    48. return c[0];
    49. }
    50. void push(const T& x)
    51. {
    52. c.push_back(x);
    53. AdjustUp(c.size() - 1);
    54. }
    55. void pop()
    56. {
    57. swap(c.back(), c[0]);
    58. c.pop_back();
    59. AdjustDown(0);
    60. }
    61. private:
    62. Container c;
    63. Compare comp;
    64. };
    65. };

    (上面priority_queue的实现的第三个模板参数其实是仿函数,有兴趣的可以去查查什么是仿函数)

    四、补充---简单说明一下deque

    deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高

    双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,给张图,让大家简单了解一下

    deque的优缺点

    • 与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
    • 与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
    • 但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构

     思考:大家可以结合deque的优点想想为什么stack和queue底层的默认容器是deque?

  • 相关阅读:
    rsync远程同步
    一文读懂spring.factories作用
    【Qt】如何在麒麟操作系统上配置开发环境
    PDF文档怎么变成链接发给别人
    MySQL事务原理之事务概述和隔离级别
    【微信小程序 | 实战开发】常用的基础内容组件介绍和使用(2)
    docker 镜像重启报错问题处理
    SpringBoot基础篇学习笔记
    js基础笔记学习199正则表达式简介2
    保存图片测试
  • 原文地址:https://blog.csdn.net/V_zjs/article/details/133363342