算法总结—栈与队列
一 基础知识
1.
均为线性表,可以由数组或链表实现
栈:先进后出,操作均在栈顶进行
队列:先进先出,队尾进,队首出
2. STL stack & queue
stack 常见操作:
s.push(x):入栈 (void类型)
s.pop(): 出栈 (void类型,只删除,不返回元素)
s.top(): 返回栈顶元素
s.size():返回栈元素个数
s.empty() :判断栈是否为空
queue 常见操作:
q.push(x): 入队
q.pop(): 出队 (void类型,只删除,不返回队首元素)
q.front(): 访问返回对首元素
q.back(): 访问返回队尾元素
q.size():返回队列元素个数
q.empty(): 判断队列是否为空
3.概念理解相关题型
3.1 Implement Stack using Queues (leetcode 225 https://leetcode.com/problems/implement-stack-using-queues/)
思路:使用两个队列实现,其中一个队列为空,用来进行颠倒顺序和输出队尾元素。
入栈操作: 向非空队列内入队即可
出栈操作:将非空队列除队尾元素外的所有元素导入另一个空队列,剩余队尾元素即为待应该待出栈元素
top()操作: 同出栈,注意只需访问返回,不需要让其出队,即仍需将其导入另一队列
注意:两队列地位平等,均可能用作储存和转移工作
class Stack { private: queue<int> q1,q2; public: // Push element x onto stack. void push(int x) { if(!q1.empty()){ q1.push(x); } else{ q2.push(x); } } // Removes the element on top of the stack. void pop() { if(!q1.empty()){ while(q1.size() > 1){ q2.push(q1.front()); q1.pop(); } q1.pop(); }else{ while(q2.size() > 1){ q1.push(q2.front()); q2.pop(); } q2.pop(); } } // Get the top element. int top() { if(!q1.empty()){ while(q1.size() > 1){ q2.push(q1.front()); q1.pop(); } int ans = q1.front(); q2.push(q1.front()); q1.pop(); return ans; }else{ while(q2.size() > 1){ q1.push(q2.front()); q2.pop(); } int ans = q2.front(); q1.push(q2.front()); q2.pop(); return ans; } } // Return whether the stack is empty. bool empty() { if(q1.empty() && q2.empty()){ return true; } return false; } };
3.2 Implement Queue using Stacks (leetcode 232 https://leetcode.com/problems/implement-queue-using-stacks/)
思路:
两个堆栈,s1负责入队,s2负责反向并出队
每次入队直接进入s1,出队操作先将s1所有元素压入s2,则栈顶元素即为待出队元素,出队后压回s1,恢复原状。
class Queue { private: stack<int> s1,s2; public: // Push element x to the back of queue. void push(int x) { s1.push(x); } // Removes the element from in front of queue. void pop(void) { while(!s1.empty()){ s2.push(s1.top()); // 注意STL栈中,top只返回不出栈,pop只出栈不返回 s1.pop(); } s2.pop(); while(!s2.empty()){ s1.push(s2.top()); s2.pop(); } } // Get the front element. int peek(void) { while(!s1.empty()){ s2.push(s1.top()); s1.pop(); } int ans = s2.top(); while(!s2.empty()){ s1.push(s2.top()); s2.pop(); } return ans; } // Return whether the queue is empty. bool empty(void) { if(s1.empty() && s2.empty()){ return true; } return false; } };
3.3 Min Stack (leetcode 155 https://leetcode.com/problems/min-stack/)
要求:实现可以返回最小元素的栈
思路:使用两个栈,一个栈s1正常出入,另一个储s2存最小值信息,当当前元素比s2栈顶元素小,入当前元素,否则再入一份栈顶元素
注意:由于s1,s2元素一样多,出栈时也应一起出栈。
class MinStack { private: stack<int> s1,s2; public: void push(int x) { s1.push(x); if(!s2.empty() && x > s2.top()){ s2.push(s2.top()); }else{ s2.push(x); } } void pop() { s1.pop(); s2.pop(); } int top() { return s1.top(); } int getMin() { return s2.top(); } };
二. 栈与队列应用 (结合数据结构教材中的应用)
1. 典型应用场合
典型应用栈的场合有: 逆序输出 (进制转换),递归嵌套(栈混洗,括号匹配),延迟缓冲(表达式计算),栈式计算(逆波兰表达式)
举例如下:
1.1 十进制数向n进制转换的栈实现
思路:除以n取余数,以此存放在栈中,输出时由栈的特点反向输出得解
void convert(stack <char>& S, int n,int base) { static char digit[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; while(n > 0){ int remainder = (int)(n % base); S.push(digit[remainder]); n /= base; } }
1.2 栈混洗(元素出入栈合法顺序判断)
思路:使用栈模拟,对于s,如果当前要出栈元素正好在栈顶,则必须出栈;否则入栈,出现矛盾则说明不合法
bool isPossible(vector<int>& in, vector<int>& out){ stack<int> s; for(int i = 0,j = 0;j<out.size();j++){ while(s.empty() || s.top!= out[j]){ if(i > in.size()){ return false; } s.push(in[++]); } s.pop(); } return true; }
补充: n个元素的合法出入栈个数为卡特兰数 (2n!)/ n! /(n+1)!
1.3 括号匹配 (leetcode 20 https://leetcode.com/problems/valid-parentheses/)
思路:遍历序列,遇到所有类型左括号,入栈;遇到右括号,判定栈顶元素与之是否匹配,匹配则出栈,不匹配则说明整体不匹配
class Solution { public: bool isValid(string s) { stack<char> S; for(int i = 0; i < s.length(); i++){ if(s[i] == '(' || s[i] == '[' || s[i] == '{'){ S.push(s[i]); continue; } if(s[i] == ')'){ if(S.empty() || S.top() != '('){ return false; } S.pop(); continue; } if(s[i] == ']'){ if(S.empty() || S.top() != '['){ return false; } S.pop(); continue; } if(s[i] == '}'){ if(S.empty() || S.top() != '{'){ return false; } S.pop(); continue; } } return S.empty(); } };
1.4 逆波兰表达式求解 (leetcode 150 https://leetcode.com/problems/evaluate-reverse-polish-notation/)
思路:根据逆波兰表达式定义,建立栈
遍历字符串,遇到数字,入栈,遇到运算符,取出栈顶两元素执行相应计算后将结果入栈
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> s; for(int i = 0;i < tokens.size();i++){ if(tokens[i] == "+" || tokens[i] == "-" || tokens[i]== "*" || tokens[i] == "/"){ int num1 = s.top(); s.pop(); int num2 = s.top(); s.pop(); if(tokens[i] == "+"){ int num = num1 + num2; s.push(num); } if(tokens[i] == "-"){ int num = num2 - num1; s.push(num); } if(tokens[i] == "*"){ int num = num2 * num1; s.push(num); } if(tokens[i] == "/"){ int num = num2 / num1; s.push(num); } } else{ s.push(atoi(tokens[i].c_str())); } } int ans = s.top(); return ans; } };