目录
1. 删除字符串中的所有相邻重复项(简单)
2. 逆波兰表达式(中等)
3. 基本计算器 II(中等)
4. 字符串解码(中等)
5. 验证栈序列(中等)
6. 小行星碰撞(中等)
单调栈:
1. 每日温度(中等)
2. 柱状图中最大的矩形(困难)
3. 最大矩形(困难)
class Solution {
public:
string removeDuplicates(string s) {
string ans; // 字符串模拟栈,这样可以直接输出答案,不用把栈转成字符串
for (auto& ch : s)
{
if (!ans.empty() && ch == ans.back())
{
ans.pop_back(); // 出栈
}
else
{
ans += ch; // 入栈
}
}
return ans;
}
};
遍历字符串数组,
遍历完字符串数组后,栈中只有一个元素就是逆波兰表达式的值。
class Solution {
public:
int evalRPN(vector& tokens) {
stack st;
for (auto& s : tokens)
{
if (s == "+" || s == "-" || s == "*" || s == "/")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(calculate(left, right, s[0]));
}
else
{
st.push(stoi(s));
}
}
return st.top();
}
private:
int calculate(int left, int right, char op)
{
switch (op)
{
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
default:
return 0;
}
}
};
先计算乘除,后计算加减:
遍历完字符串后,栈中所有元素的和就是表达式的值。
class Solution {
public:
int calculate(string s) {
vector st; // 数组模拟栈,方便最后计算栈中所有元素的和
int n = s.size();
int i = 0;
char op = '+';
while (i < n)
{
if (s[i] == ' ')
{
i++;
}
else if (s[i] >= '0' && s[i] <= '9')
{
// 将数字提取出来
int num = 0;
while (i < n && s[i] >= '0' && s[i] <= '9')
{
num = num * 10 + (s[i++] - '0');
}
if (op == '+')
{
st.push_back(num);
}
else if (op == '-')
{
st.push_back(-num);
}
else if (op == '*')
{
st.back() *= num;
}
else
{
st.back() /= num;
}
}
else
{
op = s[i];
i++;
}
}
// 计算栈中所有元素的和
int ans = 0;
for (auto& e : st)
{
ans += e;
}
return ans;
}
};
class Solution {
public:
string decodeString(string s) {
stack nums; // 数字栈
stack strs; // 字符串栈
strs.push("");
int n = s.size();
int i = 0;
while (i < n)
{
if (s[i] >= '0' && s[i] <= '9')
{
// 将数字提取出来
int num = 0;
while (i < n && s[i] >= '0' && s[i] <= '9')
{
num = num * 10 + (s[i++] - '0');
}
// 将数字压入数字栈
nums.push(num);
}
else if (s[i] == '[')
{
// 将[后面的字符串提取出来
i++;
string str;
while (i < n && s[i] >= 'a' && s[i] <= 'z')
{
str += s[i++];
}
// 将字符串压入字符串栈
strs.push(str);
}
else if (s[i] == ']')
{
// 遇到']'时,数字栈栈顶k和字符串栈栈顶s是对应的,即s重复了k次
// 将数字栈栈顶和字符串栈栈顶都出栈
// 将s重复了k次后的字符串追加到现在的字符串栈栈顶
int k = nums.top();
nums.pop();
string str = strs.top();
strs.pop();
while (k--)
{
strs.top() += str;
}
i++;
}
else
{
// 将单独的字符串(不是'['后面的字符串)提取出来
string str;
while (i < n && s[i] >= 'a' && s[i] <= 'z')
{
str += s[i++];
}
// 将字符串追加到字符串栈栈顶
strs.top() += str;
}
}
return strs.top();
}
};
遍历数组pushed,将pushed的每个元素依次入栈,每次将pushed的元素入栈之后,如果栈不为空且栈顶元素与popped的当前元素相同,则将栈顶元素出栈,同时遍历数组popped,直到栈为空或栈顶元素与popped的当前元素不同。
遍历数组pushed结束之后,每个元素都按照数组pushed的顺序入栈一次。如果栈为空,则每个元素都按照数组popped的顺序出栈,返回true。如果栈不为空,则元素不能按照数组popped的顺序出栈,返回false。
class Solution {
public:
bool validateStackSequences(vector& pushed, vector& popped) {
stack st;
int i = 0; // 用来遍历popped
for (auto& e : pushed)
{
st.push(e);
while (!st.empty() && st.top() == popped[i])
{
st.pop();
i++;
}
}
return st.empty();
}
};
使用栈模拟小行星碰撞。
遍历小行星数组asteroids,当遍历到小行星e时,使用变量alive记录小行星e是否还存在(即未爆炸)。
当小行星e存在且e < 0,栈非空且栈顶元素> 0时,说明两个小行星相互向对方移动:
重复以上判断直到不满足条件,如果最后alive为真,说明小行星e不会爆炸,则将e入栈。
class Solution {
public:
vector asteroidCollision(vector& asteroids) {
vector st; // 数组模拟栈,这样可以直接输出答案,不用把栈转成数组
for (auto e : asteroids)
{
bool alive = true;
while (alive && e < 0 && !st.empty() && st.back() > 0)
{
alive = st.back() < -e; // e 是否存在
if (st.back() <= -e)
{
st.pop_back();
}
}
if (alive)
{
st.push_back(e);
}
}
return st;
}
};
可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
遍历温度数组,如果栈不空且当前温度> 栈顶对应的温度,那么就能知道栈顶那一天下一个更高温度出现在几天后,将栈顶出栈,循环操作,直到不满足“栈不空且当前温度> 栈顶对应的温度”的条件,此时将当前下标入栈。
遍历完温度数组后,留在栈中的下标就是气温在这之后都不会升高,不用手动把其在答案数组对应的数置为0,因为最开始数组默认用0初始化。
class Solution {
public:
vector dailyTemperatures(vector& temperatures) {
stack st; // 单调栈
int n = temperatures.size();
vector ans(n);
for (int i = 0; i < n; i++)
{
while (!st.empty() && temperatures[i] > temperatures[st.top()])
{
int index = st.top();
ans[index] = i - index;
st.pop();
}
st.push(i);
}
return ans;
}
};
以某根柱子为顶的最大矩形,一定是从该柱子向两侧延伸直到遇到比它矮的柱子。
创建一个递增栈,遍历高度数组,如果当前高度> 栈顶元素,入栈;否则,栈顶出栈,并计算以栈顶的柱子为顶的最大矩形面积。由于保存在栈中的柱子的高度是递增排序的,因此栈中位于栈顶前面的一根柱子一定比位于栈顶的柱子矮,于是很容易就能找到位于栈顶的柱子两侧比它矮的柱子。
class Solution {
public:
int largestRectangleArea(vector& heights) {
int n = heights.size();
stack st; // 递增栈
st.push(-1);
int ans = 0;
for (int i = 0; i < n; ++i)
{
while (st.top() != -1 && heights[st.top()] >= heights[i])
{
int height = heights[st.top()];
st.pop();
int width = i - st.top() - 1;
ans = max(ans, height * width);
}
st.push(i);
}
while (st.top() != -1)
{
int height = heights[st.top()];
st.pop();
int width = n - st.top() - 1;
ans = max(ans, height * width);
}
return ans;
}
};
将矩阵中上下相邻的值为1的格子看成柱状图中的柱子。假设矩阵有m行n列,以矩阵的每行为基线,就可以得到m个柱状图,然后就可以计算并比较每个柱状图中的最大矩形。
class Solution {
public:
int maximalRectangle(vector>& matrix) {
int m = matrix.size(); // 行
int n = matrix[0].size(); // 列
vector heights(n, 0);
int ans = 0;
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
if (matrix[i][j] == '0')
{
heights[j] = 0;
}
else
{
heights[j]++;
}
}
ans = max(ans, largestRectangleArea(heights));
}
return ans;
}
private:
int largestRectangleArea(vector& heights) {
int n = heights.size();
stack st; // 递增栈
st.push(-1);
int ans = 0;
for (int i = 0; i < n; ++i)
{
while (st.top() != -1 && heights[st.top()] >= heights[i])
{
int height = heights[st.top()];
st.pop();
int width = i - st.top() - 1;
ans = max(ans, height * width);
}
st.push(i);
}
while (st.top() != -1)
{
int height = heights[st.top()];
st.pop();
int width = n - st.top() - 1;
ans = max(ans, height * width);
}
return ans;
}
};