栈与队列理论基础(C++)
1.队列现进先出(队尾进队头出),栈是先进后出(栈顶进出)。
2.栈提供push和pop等接口,所有元素必须符合现进后出规则,所以栈不提供走访功能,也不提供迭代器,不像是set或者map提供迭代器iterator来遍历元素。
队列先进先出的数据结构,同样不允许有遍历行为,不提供迭代器。
3.栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
4.STL中栈和队列不被归类为容器,而被归类为container adapter(容器适配器)。
5.栈的底层实现可以是vector,deque,list。
6.SGI STL中,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。(deque是一个双向队列,封住一端即可实现栈的逻辑)
SGI STL中队列底层实现缺省情况下一样使用deque实现。
//指定vector为栈的底层实现
std::stack<int,std::vector<int>> third;
//指定list为队列的底层实现
std::queue<int,std::list<int>> third;
7.栈有如下接口:push()、pop()、返回栈顶元素top()、empty()、size()。
队列有如下接口:push()、pop()、返回队头元素front()、返回队尾元素back()、empty()、size()。
用栈实现队列 leetcode 232
class MyQueue {
public:
stack<int> stackIn;
stack<int> stackOut;
MyQueue() {
}
void push(int x) {
stackIn.push(x);
}
int pop() {
if(stackOut.empty()){
while(!stackIn.empty()){
stackOut.push(stackIn.top());
stackIn.pop();
}
}
int result=stackOut.top();
stackOut.pop();
return result;
}
int peek() {
int result=this->pop();
stackOut.push(result);
return result;
}
bool empty() {
return stackIn.empty()&&stackOut.empty();
}
};
总结
1.使用两个栈,一个stackIn压入元素模仿队列入队(stackIn的栈顶就是队列的队尾),另一个栈stackOut接受stackIn弹出元素并压入栈内(stackOut的栈顶就是队列的队头)。当stackOut为空,stcakIn不为空时将stackIn中所有元素弹出并压入stackOut。
2.peek函数中,使用this指针可以避免代码重用。
用队列实现栈 leetcode 225
解法一:两个队列实现栈
class MyStack {
public:
queue<int> que1;
queue<int> que2;
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
int size=que1.size();
size--;
while(size--){
que2.push(que1.front());
que1.pop();
}
int result=que1.front();
que1.pop();
que1=que2;
while(!que2.empty()){
que2.pop();
}
return result;
}
int top() {
return que1.back();
}
bool empty() {
return que1.empty();
}
};
总结
1.队列模仿入栈,就是que1队尾入即可。模仿出栈,就相当于是先让que1队尾的元素出来,所以que2的作用就是保存que1除队尾元素之外的所有元素,然后让que1除队尾元素外都出队,再让队尾元素出队模仿出栈,再将que2的元素赋给que1,再删除que2中所有元素。
解法二:一个队列实现栈
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();
}
};
总结
1.一个队列实现栈,模仿入栈就是队列的入队,模仿出栈要先把队列的队头元素从队尾入队再从队头弹出,一开始队尾元素成为队头,此时再将队头元素弹出即可。
出现的错误
实现pop函数的时候,直接返回了que.front(),队头元素,没有将队头元素弹出。
有效的括号 leetcode 20
class Solution {
public:
bool isValid(string s) {
if(s.size()%2!=0) 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(']');
else if(sta.empty()||s[i]!=sta.top()) return false; //第二三种不匹配
else sta.pop();
}
return sta.empty(); //匹配或第一种不匹配
}
};
总结
1.一共有三种不匹配的情况,第一种就是出现了多余的左括号,第二种是左括号与右括号不匹配,第三种是出现了多余的右括号。
2.分别用栈来模拟三种不匹配的情况,遍历字符串遇到左括号那么就从栈顶压入一个相应类型的右括号,遍历到右括号就从栈中弹出一个与之对比。
第一种不匹配的情况:字符串遍历完成之后栈中还有元素。
第二种不匹配的情况:遍历到右括号时从栈中弹出一个与之对比不相同。
第三种不匹配的情况:字符串还没有遍历完但栈空了。
3.首先可以先做一个剪枝,如果字符串长度是奇数那肯定不符合返回false。
删除字符串中的所有相邻重复项 leetcode 1047
解法一:使用栈
class Solution {
public:
string removeDuplicates(string s) {
string res="";
stack<char> sta;
for(char t:s){
if(sta.empty()||t!=sta.top()) sta.push(t);
else sta.pop();
}
while(!sta.empty()){
res+=sta.top();
sta.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
总结
1.遍历字符串,如果栈为空且栈顶元素不等于当前遍历到的元素,就把遍历到的元素压入栈内,如果遍历到的元素等于栈顶元素,就弹出栈顶元素,以此实现删除相邻相同字符的操作。
2.string s,t; s+=t; 可以直接使用“+”将两个字符串拼接在一起。
解法二:使用字符串代替栈
class Solution {
public:
string removeDuplicates(string s) {
string res;
for(char t:s){
if(res.empty()||t!=res.back()) res.push_back(t);
else res.pop_back();
}
return res;
}
};
总结
1.遍历字符串s,如果字符串res为空且res尾部元素(相当于栈顶)不等于当前遍历到的元素,就把遍历到的元素放到字符串res的尾部,如果遍历到的元素等于res尾部元素,就删除res的尾部元素,以此实现删除相邻相同字符的操作。
2.最后直接返回字符串res即可(res尾部相当于栈顶,res首部相当于栈底)。