• C++:栈与队列,优先级队列


    目录

    一.栈与队列,优先级队列的介绍

    2.适配器

    二.栈与队列,优先级队列的使用

    三.栈和队列相关题目

    1.155. 最小栈

    2.栈的压入、弹出序列

    3.150. 逆波兰表达式求值

    四.模拟实现

    1.stack

    2.queue

    3.优先级队列 priority_queue

    易错点:

    重点

    (1)top()返回类型    const T& 的原因

     (2)仿函数 #include

    priority_queue代码

    4.deque与vector,list对比

    vector:

    list:   

    deque:#include

    5.总结:

    五.仿函数

    1.

     2.例子2:指向数组的原生指针,本身就是天然迭代器

    六.反向迭代器

    七.typename


    一.栈与队列,优先级队列的介绍

    2.适配器

    适配器是一种设计模式 ( 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结) 该种模式是将一个类的接口转换成客户希望的另外一个接口

    二.栈与队列,优先级队列的使用

    优先级队列的问题:优先级队列默认大的优先级高,传的是less仿函数,底层是一个大堆;
    想控制小的优先级高,传greater仿函数,底层是一个小堆。这个反过来的,算是设计的一个失误。

    (实际上less——大堆,greater——小堆,greater可理解为小堆下面越来越大)

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. namespace std
    7. {
    8. void test_stack() //栈
    9. {
    10. stack<int> s;
    11. s.push(1);
    12. s.push(2);
    13. s.push(3);
    14. s.push(4);
    15. while (!s.empty())
    16. {
    17. cout << s.top() << " ";
    18. s.pop();
    19. }
    20. cout << endl;
    21. }
    22. void test_queue() //队列
    23. {
    24. queue<int> q;
    25. q.push(1);
    26. q.push(2);
    27. q.push(3);
    28. q.push(4);
    29. while (!q.empty())
    30. {
    31. cout << q.front() << " ";
    32. q.pop();
    33. }
    34. cout << endl;
    35. }
    36. void test_priority_queue() //优先级队列
    37. {
    38. //priority_queue pq;
    39. priority_queue<int, vector<int>, greater<int>> pq;
    40. pq.push(2);
    41. pq.push(5);
    42. pq.push(1);
    43. pq.push(6);
    44. pq.push(8);
    45. while (!pq.empty())
    46. {
    47. cout << pq.top() << " ";
    48. pq.pop();
    49. }
    50. cout << endl;
    51. }
    52. }
    53. int main()
    54. {
    55. std::test_stack();
    56. std::test_queue();
    57. std::test_priority_queue();
    58. return 0;
    59. }

    三.栈和队列相关题目

    1.155. 最小栈

    思路:用两个栈,st存放入的数据,minst存放入数据后的最小数据;

    优化:minst存放入数据后的最小数据,如果后面再尾插的数据都没有这个数据大,minst就不尾插数据,如果尾插的数据 小于或等于 minst中最后一个数据,就尾插这个数据进minst。

    举例:st第一个数据5,minst尾插一个5;st尾插8,8>5,minst不变;st尾插7,minst不尾插;st尾插2,2<5,尾插2;st尾插1,1<2,minst尾插1;st又尾插1,1=1,minst还要尾插1。(因为如果=时minst不尾插,pop数据时,st尾删一个1,minst就一个1,尾删后,st中还有一个1,最小值就乱了)

    1. class MinStack {
    2. public:
    3. MinStack() {
    4. //成员变量是自定义类型,默认生成够用
    5. }
    6. void push(int val) {
    7. _st.push(val);
    8. if(_minst.empty()||val<=_minst.top())
    9. {
    10. _minst.push(val);
    11. }
    12. }
    13. void pop() {
    14. if(_st.top()==_minst.top())
    15. {
    16. _minst.pop();
    17. }
    18. _st.pop();
    19. }
    20. int top() {
    21. return _st.top();
    22. }
    23. int getMin() {
    24. return _minst.top();
    25. }
    26. private:
    27. stack<int> _st;
    28. stack<int> _minst;
    29. };

    2.栈的压入、弹出序列

    栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)

    1. class Solution {
    2. public:
    3. bool IsPopOrder(vector<int> pushV,vector<int> popV) {
    4. stack<int> st;
    5. int popi=0;
    6. for(auto e: pushV)
    7. {
    8. st.push(e);
    9. while(!st.empty() && st.top()==popV[popi])
    10. {
    11. popi++;
    12. st.pop();
    13. }
    14. }
    15. return st.empty();
    16. }
    17. };

    3.150. 逆波兰表达式求值

    过程:逆波兰表达式又称后缀表达式

      手敲:

    1. class Solution {
    2. public:
    3. int evalRPN(vector& tokens) {
    4. stack<int> st;
    5. for(auto e:tokens)
    6. {
    7. if(e=="+"||e=="-"||e=="*"||e=="/")
    8. {
    9. int b=st.top(); //易错点1
    10. st.pop();
    11. int a=st.top();
    12. st.pop();
    13. switch(e[0])
    14. {
    15. case '+':
    16. st.push(a+b);
    17. break;
    18. case '-':
    19. st.push(a-b);
    20. break;
    21. case '*':
    22. st.push(a*b);
    23. break;
    24. case '/':
    25. st.push(a/b);
    26. break;
    27. default:
    28. break;
    29. }
    30. }
    31. else
    32. {
    33. st.push(stoi(e));
    34. }
    35. }
    36. return st.top();
    37. }
    38. };

    四.模拟实现

    1.stack

    底层是vector,list,deque都可以(如果是string的话会截断)

    deque是双端队列(顺序表),适合头尾插入删除,不适合中间插入删除,不适合随机访问(虽然支持,但不适合随机访问)

    1. #pragma once
    2. namespace bit
    3. {
    4. template<class T, class Container = deque>
    5. class stack
    6. {
    7. public:
    8. void push(const T& x)
    9. {
    10. _con.push_back(x);
    11. }
    12. void pop()
    13. {
    14. _con.pop_back();
    15. }
    16. const T& top()
    17. {
    18. return _con.back();
    19. }
    20. size_t size()
    21. {
    22. return _con.size();
    23. }
    24. bool empty()
    25. {
    26. return _con.empty();
    27. }
    28. private:
    29. Container _con;
    30. };
    31. void test_stack()
    32. {
    33. //stack s;
    34. stack<int, vector<int>> s;
    35. //stack> s;
    36. //stack s; // ڽضݶʧ
    37. s.push(1);
    38. s.push(2);
    39. s.push(3);
    40. s.push(4);
    41. s.push(300);
    42. while (!s.empty())
    43. {
    44. cout << s.top() << " ";
    45. s.pop();
    46. }
    47. cout << endl;
    48. }
    49. }
    50. #include
    51. #include
    52. #include
    53. #include
    54. #include
    55. #include
    56. #include
    57. #include
    58. using namespace std;
    59. #include "Stack.h"
    60. #include "Queue.h"
    61. #include"stack.h"
    62. int main()
    63. {
    64. bit::test_stack();
    65. return 0;
    66. }

    2.queue

    底层只能是list,或deque,因为vector不支持头删

    1. namespace bit
    2. {
    3. template<class T, class Container = deque>
    4. class queue
    5. {
    6. public:
    7. void push(const T& x)
    8. {
    9. _con.push_back(x);
    10. }
    11. void pop()
    12. {
    13. _con.pop_front();
    14. }
    15. const T& front()
    16. {
    17. return _con.front();
    18. }
    19. const T& back()
    20. {
    21. return _con.back();
    22. }
    23. size_t size()
    24. {
    25. return _con.size();
    26. }
    27. bool empty()
    28. {
    29. return _con.empty();
    30. }
    31. private:
    32. Container _con;
    33. };
    34. void test_queue()
    35. {
    36. //queue q;
    37. queue<int, list<int>> q;
    38. //queue> q; // 不行
    39. q.push(1);
    40. q.push(2);
    41. q.push(3);
    42. q.push(4);
    43. while (!q.empty())
    44. {
    45. cout << q.front() << " ";
    46. q.pop();
    47. }
    48. cout << endl;
    49. }
    50. }

    3.优先级队列 priority_queue

    易错点:

    Compare仿函数用法,向上下调整函数要传参,AjustDown函数忘记过程,pop函数忘记断言不为空,仿函数内()重载忘加const

    重点

    (1)top()返回类型    const T& 的原因

    如果T是string,则返回_con[0] 又会有拷贝构造,为了提高效率就加上引用,但加上引用就可以被任意修改,防止原数组被修改,就加上const

    1. const T& top() //const T&类型的原因,详情见(1)
    2. {
    3. return _con[0];
    4. }

     (2)仿函数 #include

    就是一个类重载了运算符(),并且这个类less或greater还是一个空类,Compare comFunc; 直接定义出一个对象,comFunc(_con[parent], _con[child]) 使用起来像一个函数一样。(变量comFunc 没必要放在成员变量中,因为对象的类是空类,创建的成本小)

    1. // 仿函数/函数对象 -- 对象可以像调用函数一样去使用
    2. /*struct less
    3. {
    4. bool operator()(int x, int y)
    5. {
    6. return x < y;
    7. }
    8. };*/
    9. template<class T>
    10. struct less
    11. {
    12. bool operator()(const T& x, const T& y) const
    13. {
    14. return x < y;
    15. }
    16. };
    17. template<class T>
    18. struct greater
    19. {
    20. bool operator()(const T& x, const T& y) const
    21. {
    22. return x > y;
    23. }
    24. };
    25. template<class T, class Container = vector, class Compare = less>//仿函数
    26. class priority_queue
    27. {
    28. public:
    29. void AdjustUp(int child)
    30. {
    31. Compare comFunc; //仿函数
    32. int parent = (child - 1) / 2;
    33. while (child > 0)
    34. {
    35. //if (_con[parent] < _con[child])
    36. if (comFunc(_con[parent], _con[child])) //仿函数
    37. {
    38. swap(_con[parent], _con[child]);
    39. child = parent;
    40. parent = (child - 1) / 2;
    41. }
    42. else
    43. {
    44. break;
    45. }
    46. }
    47. }

    priority_queue代码

    1. #pragma once
    2. namespace bit
    3. {
    4. // 仿函数/函数对象 -- 对象可以像调用函数一样去使用
    5. /*struct less
    6. {
    7. bool operator()(int x, int y)
    8. {
    9. return x < y;
    10. }
    11. };*/
    12. template<class T>
    13. struct less
    14. {
    15. bool operator()(const T& x, const T& y) const
    16. {
    17. return x < y;
    18. }
    19. };
    20. template<class T>
    21. struct greater
    22. {
    23. bool operator()(const T& x, const T& y) const
    24. {
    25. return x > y;
    26. }
    27. };
    28. // 优先级队列 -- 大堆 < 小堆 >
    29. template<class T, class Container = vector, class Compare = less>//仿函数
    30. class priority_queue
    31. {
    32. public:
    33. void AdjustUp(int child)
    34. {
    35. Compare comFunc; //仿函数
    36. int parent = (child - 1) / 2;
    37. while (child > 0)
    38. {
    39. //if (_con[parent] < _con[child])
    40. if (comFunc(_con[parent], _con[child])) //仿函数
    41. {
    42. swap(_con[parent], _con[child]);
    43. child = parent;
    44. parent = (child - 1) / 2;
    45. }
    46. else
    47. {
    48. break;
    49. }
    50. }
    51. }
    52. void push(const T& x)
    53. {
    54. _con.push_back(x);
    55. AdjustUp(_con.size() - 1);
    56. }
    57. void AdjustDown(int parent)
    58. {
    59. Compare comFunc; //仿函数
    60. size_t child = parent * 2 + 1;
    61. while (child < _con.size())
    62. {
    63. //if (child+1 < _con.size() && _con[child] < _con[child+1])
    64. if (child + 1 < _con.size() && comFunc(_con[child], _con[child + 1]))
    65. { //仿函数
    66. ++child;
    67. }
    68. //if (_con[parent] < _con[child])
    69. if (comFunc(_con[parent],_con[child])) //仿函数
    70. {
    71. swap(_con[parent], _con[child]);
    72. parent = child;
    73. child = parent * 2 + 1;
    74. }
    75. else
    76. {
    77. break;
    78. }
    79. }
    80. }
    81. void pop()
    82. {
    83. assert(!_con.empty());
    84. swap(_con[0], _con[_con.size() - 1]);
    85. _con.pop_back();
    86. AdjustDown(0);
    87. }
    88. const T& top() //const T&类型的原因,详情见(1)
    89. {
    90. return _con[0];
    91. }
    92. size_t size()
    93. {
    94. return _con.size();
    95. }
    96. bool empty()
    97. {
    98. return _con.empty();
    99. }
    100. private:
    101. Container _con;
    102. };
    103. void test_priority_queue()
    104. {
    105. /*less LessCom;
    106. cout << LessCom(1, 2) << endl;
    107. greater GreaterCom;
    108. cout << GreaterCom(1, 2) << endl;*/
    109. //priority_queue pq;
    110. priority_queue<int, vector<int>, greater<int>> pq;
    111. pq.push(2);
    112. pq.push(5);
    113. pq.push(1);
    114. pq.push(6);
    115. pq.push(8);
    116. while (!pq.empty())
    117. {
    118. cout << pq.top() << " ";
    119. pq.pop();
    120. }
    121. cout << endl;
    122. }
    123. }

    4.deque与vector,list对比

    vector:

    优点:
    适合尾插尾删,随机访问,cpu高速缓存命中高
    缺点:
    a、不适合头部或者中部插入删除,效率低,需要挪动数据
    b、扩容有一定性能消耗,还可能存在一定程度的空间浪费。

    list:   

    优点:
    a.任意位置插入删除效率高。O(1) b、按需申请释放空间。
    缺点:
    不支持随机访问
    cpu高速缓存命中低


    deque:#include

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

    deque 并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个动态的二维 数组

    根据vector和list缺点升级而成,虽然deque优点均衡但是不够极致,vector的随机访问效率最高很极致,list任意插入功能机制

    优点:
    a、头部和尾部插入删除数据效率不错
    b、支持随机访问。
    C、扩容代价小。
    d、cpu高速缓存命中高
    缺点:
    a、中部插入删除效率不行
    b、虽然支持随机访问,但是效率相比vector而言还是有差距,频繁随机访问要小心

    deque适用场景:大量头尾插入删除,偶尔随机访问(说的就是栈和队列)。stack,queue用deque作为默认适配容器是ok的

    5.总结:

    stack底层:deque,vector,list(尾插尾删都支持)

    queue:deque,list(vector不支持头插头删)

    priority_queue:vector,deque(deque不推荐,list不支持随机访问)

    五.仿函数

    1.

     2.例子2:指向数组的原生指针,本身就是天然迭代器

    1. int a[6] = { 1, 2, 5, 2, 5, 7 };
    2. sort(a, a + 6);
    3. sort(a, a + 6, greater<int>());

    1. // 商品
    2. struct Goods
    3. {
    4. string _name;
    5. double _price;
    6. size_t _saleNum;
    7. //...
    8. /*bool operator<(const Goods& g) const
    9. {
    10. return _price < g._price;
    11. }*/
    12. };
    13. struct LessPrice //按价格排序
    14. {
    15. bool operator()(const Goods& g1, const Goods& g2) const
    16. {
    17. return g1._price < g2._price;
    18. }
    19. };
    20. struct GreaterPrice
    21. {
    22. bool operator()(const Goods& g1, const Goods& g2) const
    23. {
    24. return g1._price > g2._price;
    25. }
    26. };
    27. struct LessSaleNum
    28. {
    29. bool operator()(const Goods& g1, const Goods& g2) const
    30. {
    31. return g1._saleNum < g2._saleNum;
    32. }
    33. };
    34. struct GreaterSaleNum
    35. {
    36. bool operator()(const Goods& g1, const Goods& g2) const
    37. {
    38. return g1._saleNum > g2._saleNum;
    39. }
    40. };
    41. void test_functional()
    42. {
    43. vector<int> v;
    44. v.push_back(2);
    45. v.push_back(1);
    46. v.push_back(4);
    47. v.push_back(5);
    48. v.push_back(3);
    49. for (auto e : v)
    50. {
    51. cout << e << " ";
    52. }
    53. cout << endl;
    54. // less
    55. sort(v.begin(), v.end());
    56. for (auto e : v)
    57. {
    58. cout << e << " ";
    59. }
    60. cout << endl;
    61. // greater
    62. sort(v.begin(), v.end(), greater<int>());
    63. for (auto e : v)
    64. {
    65. cout << e << " ";
    66. }
    67. cout << endl;
    68. // 指向数组的原生指针,本身就是天然迭代器
    69. int a[6] = { 1, 2, 5, 2, 5, 7 };
    70. sort(a, a + 6);
    71. sort(a, a + 6, greater<int>());
    72. Goods gds[4] = { { "苹果", 2.1, 1000}, { "香蕉", 3.0, 200}, { "橙子", 2.2,300}, { "菠萝", 1.5,50} };
    73. sort(gds, gds + 4, LessPrice()); //按价格排升序 Less是升序排
    74. sort(gds, gds + 4, GreaterPrice()); //按价格排降序 Greater是降序排
    75. sort(gds, gds + 4, LessSaleNum()); //按销量排升序
    76. sort(gds, gds + 4, GreaterSaleNum()); //按销量排降序
    77. }

    六.反向迭代器

    反向迭代器跟正向迭代器相比,除了++/--时方向相反,其他操作基本一致

    自己模拟实现的:

     反向迭代器代码放在 ReverseIterator.h 中:

    1. #pragma once
    2. namespace bit
    3. {
    4. // --
    5. template<class Iterator, class Ref, class Ptr>
    6. struct Reverse_iterator
    7. {
    8. Iterator _it;
    9. typedef Reverse_iterator Self;
    10. Reverse_iterator(Iterator it)
    11. :_it(it)
    12. {}
    13. Ref operator*()
    14. {
    15. Iterator tmp = _it;
    16. return *(--tmp);
    17. }
    18. Ptr operator->()
    19. {
    20. return &(operator*());
    21. }
    22. Self& operator++()
    23. {
    24. --_it;
    25. return *this;
    26. }
    27. Self& operator--()
    28. {
    29. ++_it;
    30. return *this;
    31. }
    32. bool operator!=(const Self& s)
    33. {
    34. return _it != s._it;
    35. }
    36. };
    37. }
    1. namespace bit
    2. {
    3. template<class T>
    4. class vector
    5. {
    6. public:
    7. typedef T* iterator;
    8. typedef const T* const_iterator;
    9. // 反向迭代器适配支持
    10. typedef Reverse_iterator reverse_iterator;
    11. typedef Reverse_iteratorconst T&, const T*> const_reverse_iterator;
    12. const_reverse_iterator rbegin() const
    13. {
    14. // list_node*
    15. return const_reverse_iterator(end());
    16. }
    17. const_reverse_iterator rend() const
    18. {
    19. return const_reverse_iterator(begin());
    20. }
    21. reverse_iterator rbegin()
    22. {
    23. return reverse_iterator(end());
    24. }
    25. reverse_iterator rend()
    26. {
    27. return reverse_iterator(begin());
    28. }
    29. ……
    30. 对vector中反向迭代器的测试:
    31. void test_vector8()
    32. {
    33. vector<int> v;
    34. v.push_back(10);
    35. v.push_back(20);
    36. v.push_back(30);
    37. v.push_back(40);
    38. v.push_back(50);
    39. vector<int>::reverse_iterator rit = v.rbegin();
    40. while (rit != v.rend())
    41. {
    42. cout << *rit << " ";
    43. ++rit;
    44. }
    45. cout << endl;
    46. }

    1. template<class T>
    2. class list
    3. {
    4. typedef list_node Node;
    5. public:
    6. typedef __list_iterator iterator;
    7. typedef __list_iteratorconst T&, const T*> const_iterator;
    8. // 反向迭代器适配支持
    9. typedef Reverse_iterator reverse_iterator;
    10. typedef Reverse_iteratorconst T&, const T*> const_reverse_iterator;
    11. const_iterator begin() const
    12. {
    13. // list_node*
    14. return const_iterator(_head->_next);
    15. }
    16. const_iterator end() const
    17. {
    18. return const_iterator(_head);
    19. }
    20. iterator begin()
    21. {
    22. return iterator(_head->_next);
    23. //return _head->_next;
    24. }
    25. iterator end()
    26. {
    27. return iterator(_head);
    28. }
    29. const_reverse_iterator rbegin() const
    30. {
    31. // list_node*
    32. return const_reverse_iterator(end());
    33. }
    34. const_reverse_iterator rend() const
    35. {
    36. return const_reverse_iterator(begin());
    37. }
    38. reverse_iterator rbegin()
    39. {
    40. return reverse_iterator(end());
    41. }
    42. reverse_iterator rend()
    43. {
    44. return reverse_iterator(begin());
    45. }
    46. ……
    47. 对list中反向迭代器的测试:
    48. void test_list7()
    49. {
    50. list<int> lt;
    51. lt.push_back(1);
    52. lt.push_back(2);
    53. lt.push_back(2);
    54. lt.push_back(3);
    55. lt.push_back(4);
    56. list<int>::reverse_iterator rit = lt.rbegin();
    57. while (rit != lt.rend())
    58. {
    59. cout << *rit << " ";
    60. ++rit;
    61. }
    62. cout << endl;
    63. }

    七.typename

    类模板模板虚拟类型没有实例化之前不能去他里面找内嵌定义的类型
    类模板没有实例化,找出来也是虚拟类型,后期无法处理
    T&
    typename告诉编译器后面这一串是一个类型,等Iterator实例化以后,
    你再去他里面去找这个内嵌类型

  • 相关阅读:
    【前端缓存】localStorage是同步还是异步的?为什么?
    5.2 Ajax 数据爬取实战
    国际经济学 简答计算
    35岁危机来临前,程序员如何未雨绸缪?
    共识算法-Mencius详解
    发送QQ邮件
    Linux:进程(二)
    ansible镜像构建使用
    Swin Transformer:最佳论文,准确率和性能双佳的视觉Transformer | ICCV 2021
    堆优化迪氏最短单源路径原理及C++实现
  • 原文地址:https://blog.csdn.net/zhang_si_hang/article/details/126067468