【LeetCode】栈精选9题

本文介绍了多个与IT技术相关的编程题目,包括字符串操作(删除重复、解码),算术表达式计算(逆波兰表达式、基本计算器),数据结构应用(验证栈序列、单调栈、柱状图中的最大矩形),以及一个实际问题(每日温度分析)。
摘要由CSDN通过智能技术生成

目录

1. 删除字符串中的所有相邻重复项(简单)

2. 逆波兰表达式(中等)

3. 基本计算器 II(中等)

4. 字符串解码(中等)

5. 验证栈序列(中等)

6. 小行星碰撞(中等)

单调栈:

1. 每日温度(中等)

2. 柱状图中最大的矩形(困难)

3. 最大矩形(困难)


1. 删除字符串中的所有相邻重复项(简单)

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;
    }
};

2. 逆波兰表达式(中等)

遍历字符串数组,

  • 如果遇到操作数,将操作数入栈
  • 如果遇到运算符,将两个操作数出栈,其中先出栈的是右操作数,后出栈的是左操作数,然后计算,然后将计算结果入栈

遍历完字符串数组后,栈中只有一个元素就是逆波兰表达式的值。

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> 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;
        }
    }
};

3. 基本计算器 II(中等)

先计算乘除,后计算加减:

  • 当一个数前面是 + 时,该数入栈
  • 当一个数前面是 - 时,该数的相反数入栈
  • 当一个数前面是 * / 时,将该数与栈顶元素进行计算,并将栈顶元素替换为计算结果

遍历完字符串后,栈中所有元素的和就是表达式的值。

class Solution {
public:
    int calculate(string s) {
        vector<int> 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;
    }
};

4. 字符串解码(中等)

class Solution {
public:
    string decodeString(string s) {
        stack<int> nums; // 数字栈
        stack<string> 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();
    }
};

5. 验证栈序列(中等)

遍历数组pushed,将pushed的每个元素依次入栈,每次将pushed的元素入栈之后,如果栈不为空且栈顶元素与popped的当前元素相同,则将栈顶元素出栈,同时遍历数组popped,直到栈为空或栈顶元素与popped的当前元素不同。

遍历数组pushed结束之后,每个元素都按照数组pushed的顺序入栈一次。如果栈为空,则每个元素都按照数组popped的顺序出栈,返回true。如果栈不为空,则元素不能按照数组popped的顺序出栈,返回false。

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> 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();
    }
};

6. 小行星碰撞(中等)

使用栈模拟小行星碰撞。

遍历小行星数组asteroids,当遍历到小行星e时,使用变量alive记录小行星e是否还存在(即未爆炸)。

当小行星e存在且e < 0,栈非空且栈顶元素> 0时,说明两个小行星相互向对方移动:

  1. 先看e是否存在:栈顶元素 < -e,e存在;栈顶元素 >= -e,e爆炸。
  2. 再看栈顶元素是否存在:如果栈顶元素<= -e,则栈顶元素爆炸,执行出栈操作。

重复以上判断直到不满足条件,如果最后alive为真,说明小行星e不会爆炸,则将e入栈。

class Solution {
public:
    vector<int> asteroidCollision(vector<int>& asteroids) {        
        vector<int> 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;
    }
};

单调栈:

1. 每日温度(中等)

可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。

遍历温度数组,如果栈不空且当前温度> 栈顶对应的温度,那么就能知道栈顶那一天下一个更高温度出现在几天后,将栈顶出栈,循环操作,直到不满足“栈不空且当前温度> 栈顶对应的温度”的条件,此时将当前下标入栈。

遍历完温度数组后,留在栈中的下标就是气温在这之后都不会升高,不用手动把其在答案数组对应的数置为0,因为最开始数组默认用0初始化。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> st; // 单调栈
        int n = temperatures.size();
        vector<int> 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;
    }
};

2. 柱状图中最大的矩形(困难)

以某根柱子为顶的最大矩形,一定是从该柱子向两侧延伸直到遇到比它矮的柱子。

创建一个递增栈,遍历高度数组,如果当前高度> 栈顶元素,入栈;否则,栈顶出栈,并计算以栈顶的柱子为顶的最大矩形面积。由于保存在栈中的柱子的高度是递增排序的,因此栈中位于栈顶前面的一根柱子一定比位于栈顶的柱子矮,于是很容易就能找到位于栈顶的柱子两侧比它矮的柱子。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> 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;
    }
};

3. 最大矩形(困难)

将矩阵中上下相邻的值为1的格子看成柱状图中的柱子。假设矩阵有m行n列,以矩阵的每行为基线,就可以得到m个柱状图,然后就可以计算并比较每个柱状图中的最大矩形。

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        int m = matrix.size();    // 行
        int n = matrix[0].size(); // 列
        vector<int> 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<int>& heights) {
        int n = heights.size();
        stack<int> 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;
    }
};
  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值