【算法】栈专题
一. (1047.) 删除字符串中的所有相邻重复项
算法原理:
关键点是当前元素是否被消除,需要知道上一个元素的信息。完美符合栈的特性,后进先出,后入栈的元素和栈顶元素相比较,等于就使栈顶元素出栈,不等于就自己进栈,但本题要求返回string,用栈得到结果后还要转化为string,可以直接用数组来模拟栈,在数组的尾部进行尾插尾删,实现栈的进栈和出栈
class Solution {
public:string removeDuplicates(string s) {string ret;//用数组模拟栈for(auto c:s){//注意字符串为空时会非法访问if(ret.size()&&c==ret.back()) ret.pop_back();//模拟出栈else ret+=c;//模拟入栈}return ret;}
};
二. (844.) 比较含退格的字符串
算法原理:
由于退格的时候需要知道前⾯元素的信息,⽽且退格也符合后进先出的特性。因此我们可以使⽤「栈」结构来模拟退格的过程。与上题思路相同
当遇到⾮ # 字符的时候,直接进栈;
当遇到 # 的时候,栈顶元素出栈
class Solution {
public:bool backspaceCompare(string s, string t) {string ret1,ret2;for(auto c:s){if(ret1.size()&&c=='#') ret1.pop_back();//字符串为空时不需要退格if(c!='#') ret1+=c;//结果中部包含退格}for(auto c:t){if(ret2.size()&&c=='#') ret2.pop_back();if(c!='#') ret2+=c;}if(ret1==ret2) return true;else return false;}
};
三. (227.) 基本计算器 II
算法原理:
本题表达式中没有括号,只用处理加减乘除混合运算即可,先乘除后加减。分情况讨论,表达式中字符只有三种情况,分别为空格、运算符和数字,数字注意要提取链接起来,因为有可能是多位数的运算。可以先设置一个变量记为’+',每遇到运算符就更新。用数组模拟的栈中存储元素,遇到乘除就及时计算,减号就转化为负数入栈,最终就可以不用考虑每个数之间的加减关系,全部相加即为结果。
除此之外还有两栈处理和逆波兰表达式的思路
class Solution {
public:int calculate(string s) {vector<int> st;//用数组模拟栈char c='+';//保存每一个数的前一个符号int i=0;while(i<s.size()){//分三种情况讨论if(s[i]==' ') i++;//1.为空格时else if(s[i]>='0'&&s[i]<='9')//2.为数字时{//提取数字int tmp=0;//每次循环前tmp必须重置,因为tmp值涉及前一值和当前值while(i<s.size()&&s[i]>='0'&&s[i]<='9') tmp=(tmp*10+(s[i++]-'0'));if(c=='+') st.push_back(tmp);else if(c=='-') st.push_back(-tmp);else if(c=='*') st.back()*=tmp;else st.back()/=tmp;}else{//3.为运算符时c=s[i++];}}//同一加法处理返回值int ret=0;for(auto n:st) ret+=n;return ret;}
};
四. (394.) 字符串解码
算法原理:
注意:
1.用双栈来实现,每次遇到’]'时拿出字符栈顶元素(记为m)和数字字符栈顶元素(记为n),让m拼接n次,pop掉字符和数字栈原来的栈顶元素,push到新的栈顶元素后
2.当遍历到左右括号时要跳过括号进行提取操作
3.提取字符串与提取数字的操作相同
其余细节见代码
class Solution {
public:string decodeString(string s) {stack<int> nums;stack<string> st;st.push("");//防止越界int i=0,n=s.size();//遍历s的下标while(i<n){//提取数字并入栈if(s[i]>='0'&&s[i]<='9'){int tmp=0;while(i<n&&s[i]>='0'&&s[i]<='9') tmp=tmp*10+(s[i++]-'0');nums.push(tmp);}//遇到'[',提取字符入栈else if(s[i]=='['){i++;//跳过括号string str;while(i<n&&s[i]>='a'&&s[i]<='z') str+=s[i++];st.push(str);}//判断括号,右括号第一次出现else if(s[i]==']'){string str=st.top();//拼接重复字符串int k=nums.top();nums.pop();//用完数字和字符栈顶元素后要删除st.pop();while(k--){ //将拼接后字符串放入上一个栈元素后面st.top()+=str;}i++;//跳过右括号}//遇到单独的字符,提取出来放到st栈顶元素后else while(i<n&&s[i]>='a'&&s[i]<='z') st.top()+=s[i++];}return st.top();}
};
五. (946. 验证栈序列)
算法思路:
⽤栈来模拟进出栈的流程。
⼀直让元素进栈,进栈的同时判断是否需要出栈。当所有元素模拟完毕之后,如果栈中还有元素,那么就是⼀个⾮法的序列。否则,就是⼀个合法的序列。或者用出栈的指针判断是否遍历完数组,完则正确,反之
class Solution {
public:bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {vector<int> st;//模拟栈int i=0;//弹出数组指针for(auto n:pushed){st.push_back(n);while(i<popped.size()&&!st.empty()&&st.back()==popped[i]) st.pop_back(),i++;//else st.push_back(n);}if(st.empty()) return true;else return false;}
};