目录
栈和队列
栈的用途
可以⽤Stack作为辅助,实现深度优先算法(Depth first search,
DFS),或者将递归转为while循环递归本⾝就是相当于把函数本⾝⼀层⼀层加到操作系统的内存栈上⼊栈操作相当于递归调⽤⾃⾝,出栈操作相当于递归返回。
队列的用途
我们可以⽤Queue作为辅助,实现⼲度优先算法(Breadth first search, BFS)Queue还可以作为buffer,构建⼀个⽣产者-消费者模型:⽣产者把新的元素加到队尾,消费者从队头读取元素。在有两个线程同时读取同⼀个queue时,需要考虑同步(synchronization)
stack 与 queue 可以视作封装好的Linked list,只是限制了访问和插⼊的⾃由。适⽤stack或queue的情境,也可以考虑使⽤更为强⼤的list。
1. 通过stack实现特殊顺序的读取
由于stack具有LIFO的特性,如需实现任何特定顺序的读取操作,往往可以借助两个stack互相”倾倒"来实现特定顺序。另⼀个stack作为辅助。
232. 用栈实现队列
链接: 232. 用栈实现队列.
class MyQueue {
private:
stack<int> inStack, outStack;
void in2out()
{
while(!inStack.empty())
{
outStack.push(inStack.top());
inStack.pop();
}
}
public:
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
inStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if(outStack.empty())
{
in2out();
}
int x = outStack.top();
outStack.pop();
return x;
}
/** Get the front element. */
int peek() {
if (outStack.empty()) {
in2out();
}
return outStack.top();
}
/** Returns whether the queue is empty. */
bool empty() {
return inStack.empty() && outStack.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
面试题 03.05. 栈排序
链接: 面试题 03.05. 栈排序.
“save or later”问题
有⼀类问题有这样的特性:当前节点的解依赖后驱节点。
对于某个当前节点,如果不能获知后驱节点,就⽆法得到有意义的解。这类问题可以通过stack(或等同于stack的若⼲个临时变量)
解决:先将当前节点⼊栈,然后看其后继节点的值,直到其依赖的所有节点都完备时,再从栈中弹出该节点求解。某些时候,甚⾄需要反复这个过程:将当前节点的计算结果再次⼊栈,直到其依赖的后继节点完备。
20. 有效的括号
链接: 20. 有效的括号.
class Solution {
public:
bool isValid(string s) {
if(s.size() % 2) { return false; }
stack<int> 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() || s[i] != st.top()) { return false; }
else { st.pop(); }
}
return st.empty();
}
};
3. ⽤stack解决Top-Down结构的问题
所谓的Top-Down结构,从逻辑理解的⾓度来看,实际上就是⼀种树形结构,从顶层出发,逐渐向下扩散,例如⼆叉树的周游问题。 在实际运算的时候,我们先解决⼦问题,再利⽤⼦问题的结果解决当前问题。由于Stack的LIFO特性,可以利⽤Stack数据结构消除递归。Recursion通常⽤函数调⽤⾃⾝实现,在调⽤的时候系统会分配额外的空间,并且需要⽤指针记录返回位置,故overhead比较大。