今日内容: 理论基础、232.用栈实现队列、225. 用队列实现栈、20. 有效的括号、1047. 删除字符串中的所有相邻重复项
理论基础
栈和队列是STL(C++标准库)中的两个数据结构
栈stack-先进后出;队列queue-先进先出
数据结构问题
1. C++中stack 是容器么?
答:不是。栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可选择的,如vector, deque, list。STL栈和队列不是容器,而被归为容器适配器(container adapter)
stack要求其底层容器需满足如下操作:empty:判空操作;back:获取尾部元素操作; push_back:尾部插入元素操作; pop_back:尾部删除元素操作 (这里的尾部相当于stack的顶部)
2. 我们使用的stack是属于哪个版本的STL?
答:常用SGI STL,该版本下如果没有指定底层实现的话,默认是以deque为栈的底层结构。
3. 我们使用的STL中stack是如何实现的?(队列也是如此)
答:栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可选择的,如vector, deque, list,默认使用deque为底层容器。(deque是双向队列,只要封住一端就可实现栈的逻辑)
4. stack 提供迭代器来遍历stack空间么?
答:stack没有迭代器。stack的所有元素进出都必须符合“先进后出”的条件,只有stack顶端的元素,才有机会被外界取用。stack不提供随机访问功能,也不提供迭代器。
初始化栈和队列
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
232.用栈实现队列
思路
用两个栈,即可模拟队列的功能。队列是输入1-2-3,输出1-2-3;而第一个栈输入1-2-3,输出3-2-1给第二栈,故第二个栈可以输出1-2-3
进队列和进in栈是一样的,所以问题关键是出队列时,要先把in栈数据拿到out栈,再从out栈出
stack基本用法
代码
class MyQueue {
public:
stack<int> inStack;
stack<int> outStack;
MyQueue() {
}
void push(int x) {
inStack.push(x);
}
int pop() {
if(outStack.empty()){
while(!inStack.empty()){
outStack.push(inStack.top());
inStack.pop();
}
}
int result = outStack.top();
outStack.pop();
return result;
}
// 与pop()类似,只是查询第一个进入的元素,而不用删除
int peek() {
int result = this->pop();
outStack.push(result); //因为pop()把元素弹了出去,所以要加回来
return result;
}
bool empty() {
return inStack.empty() && outStack.empty();
}
};
top()是获取栈的顶头元素,pop()才是把它弹出
peek()与pop()类似,所以尽量采用代码复用的方式而不是粘贴代码,提高美观度
225. 用队列实现栈
思路:用一个队列来实现顺序的颠倒
队列的基本用法
q.push(item) //将item压入队列尾部
q.pop() //删除队首元素,但不返回
q.front() //返回队首元素,但不删除
q.back() //返回队尾元素,但不删除
q.size() //返回队列中元素的个数
q.empty() //检查队列是否为空,如果为空返回true,否则返回false
代码
class MyStack {
public:
queue<int> q;
MyStack() {
}
void push(int x) {
q.push(x);
}
int pop() {
// 除最后一个元素外,把每个头部元素添加到尾部
int size = q.size();
while(--size){
q.push(q.front());
q.pop();
}
int result = q.front();
q.pop();
return result;
}
int top() {
return q.back();
}
bool empty() {
return q.empty();
}
};
20. 有效的括号
栈的经典应用(匹配问题都是栈的强项)
思路
其实只有3种不匹配的情况
1-左括号有多余的情况
解决:当遍历到左括号时,把对应的右括号放到栈中(为了方便对比匹配),如果遇到右括号,就把栈中的右括号弹出,当遍历结束后,栈中还有元素的话,则说明情况1成立,左括号有多余的情况
2-括号没有多余,但括号类型没有匹配上
解决:当遍历到左括号时,把对应的右括号放到栈中(这一点与情况一相同),但当遇到右括号时,要和顶元素去比较是否一样,若不一样,说明情况2成立
3-右括号有多余的情况
解决:当遍历到左括号时,把对应的右括号放到栈中(这一点与情况一相同),但若还未遍历完,栈就空了,则说明情况3成立
代码
class Solution {
public:
bool isValid(string s) {
// 剪枝操作 如果长度为奇数,则一定失败
if(s.size()%2) return false;
stack<char> sta;
for(int i=0; i<s.size(); i++){
if(s[i] == '(') sta.push(')');
else if(s[i] == '[') sta.push(']');
else if(s[i] == '{') sta.push('}');
// 分别对应情况3 右括号多余 和情况2 类型不匹配
// 这里写或是因为如果栈为空,则.top()为非法操作
else if(sta.empty() || sta.top() != s[i]) return false;
else sta.pop();
}
// 对应情况1,如果不为空,则说明左括号多余
return sta.empty();
}
};
若长度为奇数,则一定不匹配,可以做剪枝操作
1047. 删除字符串中的所有相邻重复项
栈的经典应用(匹配问题都是栈的强项)
class Solution {
public:
string removeDuplicates(string s) {
stack<char> sta;
for(int i=0; i<s.size(); i++){
// 如果不等于栈顶或者栈为空,则放入
if(sta.empty() || s[i]!=sta.top()){
sta.push(s[i]);
}
// 如果等于栈顶则弹出栈顶
else{
sta.pop();
}
}
// 从栈导出到字符串,并且翻转
string str="";
while(!sta.empty()){
str += sta.top();
sta.pop();
}
reverse(str.begin(), str.end());
return str;
}
};