1. 前言(栈适用于解哪些题?)
栈适合解决需要后进先出(LIFO)的结构的算法题,例如:
- 括号匹配问题:判断给定字符串中括号是否匹配。
- 表达式求值问题:将表达式转换为后缀表达式,并计算其值。
- 逆波兰表达式问题:将表达式转换为逆波兰表达式,并计算其值。
- 直方图最大矩形面积问题:给定一个直方图,求最大的矩形面积。
- 进制转换问题:将一个十进制数转换为任意进制的数。
- 迷宫问题:在迷宫中寻找从起点到终点的路径。
我们下面会选择一部分题并用栈来进行解题:
2. 算法题
1047.删除字符串中的所有相邻重复项
思路
-
题意分析:要求将字符串中所有连续的字符删去,如下图所示:
-
解法:用栈模拟这个过程
- 创建一个string类来作为栈
- 将s中的元素依次入栈,每次判断站栈顶元素是否等于当前元素
- 如果是,则删去栈顶元素,如果不是则将该元素入栈
代码
string removeDuplicates(string s) {
// 解法:用栈模拟过程
string st;
for(char ch : s)
{
if(!st.empty() && st.back() == ch)
st.pop_back();
else
st += ch;
}
return st;
}
844.比较含退格的字符串
思路
- 题意分析:即比较两个字符串在退格后是否相同,
- 解法:用栈模拟这个过程
- 分别对两个字符串创建栈来模拟(用字符串表示栈)
- 如果 遇到#且栈不为空,则删除栈顶元素,否则将当前元素入栈。
代码
bool backspaceCompare(string s, string t) {
// 栈模拟退格删除元素过程
string st1 = "";
for(char ch : s)
{
if(ch != '#')
st1.push_back(ch);
else if(ch == '#' && !st1.empty())
st1.pop_back();
}
string st2 = "";
for(char ch : t)
{
if( ch != '#')
st2.push_back(ch);
else if(ch == '#' && !st2.empty())
st2.pop_back();
}
return st1 == st2;
}
227.基本计算器II
思路
-
题意分析:该题属于一个表达式求值题,即计算一个表示表达式的字符串的值。该题中s仅由’+‘、’-‘、’*‘、’/’ 四个符号组成。
-
解法:利用栈进行计算(数组表示栈)
- 上面的是使用栈解决这道题的总体算法思想,下面是步骤细节注意.
- 定义sym用于记录符号字符,数组表示栈
- 从头遍历字符串,根据当前字符进入不同分支
- 如果是空格,直接跳过该位,i++
- 如果是符号,则sym记录当前符号后,i++
- 如果是数字:
- 由于表达式中的数不一定是个位数,所以先利用循环找到当前位置的数
- 之后根据sym存储的符号对tmp和栈顶元素进行处理(即图中步骤)
- 最后累加元素
代码
int calculate(string s) {
// 用一个栈存储数字字符,定义sym记录遇到的符号
// sym='+' : 直接将下一位tmp加到栈中
// sym='-' : 将-tmp加入到栈中
// sym='*' : 栈顶元素 *= tmp
// sym='/' : 栈顶元素 /= tmp
char sym = '+';
vector<int> st; // 数组作栈
int i = 0, n = s.size();
while(i < n)
{
if(s[i] == ' ') i++;
else if(s[i] >= '0' && s[i] <= '9')
{
int tmp = 0;
while(s[i] >= '0' && s[i] <= '9')
// 循环:tmp记录当前数字 并将其由字符串转为整形)
tmp = tmp * 10 + (s[i++] - '0');
if(sym == '+') st.push_back(tmp);
else if(sym == '-') st.push_back(-tmp);
else if(sym == '*') st.back() *= tmp;
else st.back() /= tmp;
}
else
{
sym = s[i++];
}
}
int ret = 0;
// 将栈中所有元素累加
for(auto x : st)
ret += x;
return ret;
}
394.字符串解码
思路
-
题意分析:看示例便很好理解,通过将 “数字[字符]” 解码为 “数字次字符串”
-
解法:利用栈进行计算/font>
- 上图为用栈解决该题的总体算法思路,下面是简单的部分注意事项
- 遇到 ‘[’ 时,我们向 “字符串栈” 加入空串,便于后面的字符串加入到"字符串栈"中
- 最后结果就是字符串的栈顶元素
代码
string decodeString(string s) {
// 栈模拟过程 : 字符串栈sst 整形栈 ist
vector<string> sst;
sst.push_back(""); // 初始化栈顶元素为空串
vector<int> ist;
// 遍历字符串,四种情况:
// 1. 遇到数字:放入ist
// 2. 遇到字符串 : 放到sst栈顶元素的后一位
// 3. 遇到左括号 : 将空字符串 "" 加入到 sst 的栈顶
// 4. 遇到右括号 : 合并两栈栈顶元素(即重复字符串)
int i = 0, n = s.size();
string tmp = "";
while(i < n)
{
if(s[i] >= '0' && s[i] <= '9')
{
int num = 0;
// 数字并不一定是个位、
while(isdigit(s[i]))
num = num * 10 + (s[i++] - '0');
ist.push_back(num);
continue;
}
else if(s[i] >= 'a' && s[i] <= 'z')
{
tmp = "";
while(s[i] >= 'a' && s[i] <= 'z' && i < n)
tmp += s[i++]; // 记录该字符串
sst[sst.size() - 1] += tmp; // 加到栈顶后一位
continue;
}
else if(s[i] == '[')
{
sst.push_back(""); // 新建空串
}
else // s[i] == ']'
{
// 合并两栈顶元素(重复字符串)
tmp = sst.back();
int x = ist.back();
ist.pop_back();
sst.pop_back();
while(x--)
sst.back() += tmp;
}
++i;
}
return sst.back();
}
946.验证栈序列
思路
- 题意分析:标准的用栈模拟过程的题,即根据题目给出的pushed和poped数组来判断该输入、弹出顺序是否合法。
- 解法:利用栈模拟过程
- 定义两指针分别表示当前待入栈元素的位置和当前要弹出的元素在出栈序列中的位置
- 进入循环,判断当前的状态是否合法
- 合法则将该元素弹出栈
- 不合法则将当前待入栈元素压入辅助栈中,并将cur1指针后移
- 当所有的元素都入栈之后,如果辅助栈中还有元素,说明出栈序列不合法
代码
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st; // 辅助栈
int cur1 = 0, cur2 = 0;
while (cur2 < popped.size())
{ // 出栈序列结束,则合法
if (!st.empty() && st.top() == popped[cur2])
{
st.pop();
cur2++;
}
else if (cur1 < pushed.size())
st.push(pushed[cur1++]);
else
return false;
}
return true;
}