本文为学习代码随想录 时所做的笔记,仅供学习参考,不做任何商业用途,若有侵权,请联系删除。
栈与队列理论基础 
C++标准库是有多个版本的,要知道我们使用的STL是哪个版本,才能知道对应的栈和队列的实现原理。
HP STL 其他版本的C++ STL,一般是以HP STL为蓝本实现出来的,HP
STL是C++ STL的第一个实现版本,而且开放源代码。 
P.J.Plauger STL 由P.J.Plauger参照HP STL实现出来的,被Visual
C++编译器所采用,不是开源的。 
SGI STL 由Silicon Graphics Computer Systems公司参照HP
STL实现,被Linux的C++编译器GCC所采用,SGI
STL是开源软件,源码可读性甚高。 
 
 
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。 
所以STL中栈往往不被归类为容器,而被归类为container
adapter(容器适配器)。
从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list
都是可以的, 主要就是数组和链表的底层实现。
我们常用的SGI
STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的低层结构。 
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
SGI STL中
队列底层实现缺省情况下一样使用deque实现的。 
我们也可以指定vector为栈的底层实现,初始化语句如下:
1 std::stack<int , std::vector<int > > third;  
 
刚刚讲过栈的特性,对应的队列的情况是一样的。
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器,
SGI STL中队列一样是以deque为缺省情况下的底部结构。 
也可以指定list 为起底层实现,初始化queue的语句如下:
1 std::queue<int , std::list<int >> third; 
 
所以STL 队列也不被归类为容器,而被归类为container adapter(
容器适配器)。
用栈实现队列 
232.
用栈实现队列 - 力扣(LeetCode) 
在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入) ,再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class  MyQueue  {public :     stack<int > stIn;     stack<int > stOut;          MyQueue () {     }          void  push (int  x)   {         stIn.push (x);     }          int  pop ()   {                  if  (stOut.empty ()) {                          while (!stIn.empty ()) {                 stOut.push (stIn.top ());                 stIn.pop ();             }         }         int  result = stOut.top ();         stOut.pop ();         return  result;     }          int  peek ()   {         int  res = this ->pop ();          stOut.push (res);          return  res;     }          bool  empty ()   {         return  stIn.empty () && stOut.empty ();     } };
 
定要懂得复用,功能相近的函数要抽象出来,不要大量的复制粘贴,很容易出问题! 
用队列实现栈 
225.
用队列实现栈 - 力扣(LeetCode) 
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外)
重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class  MyStack  {public :     queue<int > que;          MyStack () {     }          void  push (int  x)   {         que.push (x);     }          int  pop ()   {         int  size = que.size ();         size--;         while  (size--) {              que.push (que.front ());             que.pop ();         }         int  result = que.front ();          que.pop ();         return  result;     }          int  top ()   {         return  que.back ();     }          bool  empty ()   {         return  que.empty ();     } };
 
有效的括号 
20.
有效的括号 - 力扣(LeetCode) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :     bool  isValid (string s)   {         if  (s.size () % 2  != 0 ) return  false ;          stack<char > st;         for  (int  i = 0 ; i < s.size (); i++) {             if  (s[i] == '(' ) st.push (')' );             else  if  (s[i] == '{' ) st.push ('}' );             else  if  (s[i] == '[' ) st.push (']' );                                       else  if  (st.empty () || st.top () != s[i]) return  false ;             else  st.pop ();          }                  return  st.empty ();     } };
 
删除字符串中的所有相邻重复项 
1047.
删除字符串中的所有相邻重复项 - 力扣(LeetCode) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Solution  {public :     string removeDuplicates (string S)   {         stack<char > st;         for  (char  s : S) {             if  (st.empty () || s != st.top ()) {                 st.push (s);             } else  {                 st.pop ();              }         }         string result = "" ;         while  (!st.empty ()) {              result += st.top ();             st.pop ();         }         reverse  (result.begin (), result.end ());          return  result;     } };
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :     string removeDuplicates (string S)   {         string result;         for (char  s : S) {             if (result.empty () || result.back () != s) {                 result.push_back (s);             }             else  {                 result.pop_back ();             }         }         return  result;     } };
 
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中 ,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
而且在企业项目开发中,尽量不要使用递归 !在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!) 
逆波兰表达式求值 
150.
逆波兰表达式求值 - 力扣(LeetCode) 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :     int  evalRPN (vector<string>& tokens)   {         stack<int > st;         for  (int  i = 0 ; i < tokens.size (); i++) {             if  (tokens[i] == "+"  || tokens[i] == "-"  || tokens[i] == "*"  || tokens[i] == "/" ) {                 int  num1 = st.top ();                 st.pop ();                 int  num2 = st.top ();                 st.pop ();                 if  (tokens[i] == "+" ) st.push (num2 + num1);                 if  (tokens[i] == "-" ) st.push (num2 - num1);                 if  (tokens[i] == "*" ) st.push (num2 * num1);                 if  (tokens[i] == "/" ) st.push (num2 / num1);             } else  {                 st.push (stoi (tokens[i]));             }         }         int  result = st.top ();         st.pop ();          return  result;     } };
 
滑动窗口最大值 
239.
滑动窗口最大值 - 力扣(LeetCode) 
思路 
此时我们需要一个队列,这个队列呢,放进去窗口里的元素,然后随着窗口的移动,队列也一进一出,每次移动之后,队列告诉我们里面的最大值是什么。
这个队列应该长这个样子:
1 2 3 4 5 6 7 8 9 10 class  MyQueue  {public :     void  pop (int  value)   {     }     void  push (int  value)   {     }     int  front ()   {         return  que.front ();     } };
 
每次窗口移动的时候,调用que.pop(滑动窗口中移除元素的数值),que.push(滑动窗口添加元素的数值),然后que.front()就返回我们要的最大值。
其实队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里里的元素数值是由大到小的。 
那么这个维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。C++中没有直接支持单调队列,需要我们自己来一个单调队列 
不要以为实现的单调队列就是
对窗口里面的数进行排序,如果排序的话,那和优先级队列又有什么区别了呢。 
对于窗口里的元素{2, 3, 5, 1 ,4},单调队列里只维护{5, 4}
就够了,保持单调队列里单调递减,此时队列出口元素就是窗口里最大元素。
此时大家应该怀疑单调队列里维护着{5, 4} 怎么配合窗口经行滑动呢?
设计单调队列的时候,pop,和push操作要保持如下规则:
pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作 
push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止 
 
保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。
nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  MyQueue  { public :     deque<int > que;                void  pop (int  value)   {         if  (!que.empty () && value == que.front ()) {             que.pop_front ();         }     }               void  push (int  value)   {         while  (!que.empty () && value > que.back ()) {             que.pop_back ();         }         que.push_back (value);     }          int  front ()   {         return  que.front ();     } };
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class  Solution  {private :     class  MyQueue  {      public :         deque<int > que;                            void  pop (int  value)   {             if  (!que.empty () && value == que.front ()) {                 que.pop_front ();             }         }                           void  push (int  value)   {             while  (!que.empty () && value > que.back ()) {                 que.pop_back ();             }             que.push_back (value);         }                  int  front ()   {             return  que.front ();         }     };public :     vector<int > maxSlidingWindow (vector<int >& nums, int  k)   {         MyQueue que;         vector<int > result;         for  (int  i = 0 ; i < k; i++) {              que.push (nums[i]);         }         result.push_back (que.front ());          for  (int  i = k; i < nums.size (); i++) {             que.pop (nums[i - k]);              que.push (nums[i]);              result.push_back (que.front ());          }         return  result;     } };
 
前k个高频元素 
347.
前 K 个高频元素 - 力扣(LeetCode) 
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 
如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
有的同学一想,题目要求前 K 个高频元素,那么果断用大顶堆啊。
那么问题来了,定义一个大小为k的大顶堆,在每次移动更新大顶堆的时候,每次弹出都把最大的元素弹出去了,那么怎么保留下来前K个高频元素呢。
而且使用大顶堆就要把所有元素都进行排序,那能不能只排序k个元素呢?
所以我们要用小顶堆,因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class  Solution  {public :          class  mycomparison  {     public :         bool  operator () (const  pair<int , int >& lhs, const  pair<int , int >& rhs)   {             return  lhs.second > rhs.second;         }     };     vector<int > topKFrequent (vector<int >& nums, int  k)   {                  unordered_map<int , int > map;          for  (int  i = 0 ; i < nums.size (); i++) {             map[nums[i]]++;         }                           priority_queue<pair<int , int >, vector<pair<int , int >>, mycomparison> pri_que;                  for  (unordered_map<int , int >::iterator it = map.begin (); it != map.end (); it++) {             pri_que.push (*it);             if  (pri_que.size () > k) {                  pri_que.pop ();             }         }                  vector<int > result (k)  ;         for  (int  i = k - 1 ; i >= 0 ; i--) {             result[i] = pri_que.top ().first;             pri_que.pop ();         }         return  result;     } };