【LeetCode 手撕算法】(栈)有效括号、最小栈、字符串解码、每日温度、柱状图最大矩形

|-------------|--------------|-----------------|
| push | 压栈,将元素放入栈顶 | push(e) |
| pop | 弹栈,移除并返回栈顶元素 | pop() |
| peek | 查看栈顶元素(不移除) | peek() |
| isEmpty | 判断栈是否为空 | isEmpty() |
| size | 获取栈中元素个数 | size() |

坑点 说明 解决方案
pop/peek 前未判空 空栈调用会抛 EmptyStackException 用前检查 !stack.isEmpty()
== 比较 Integer 比较两个 Integer 对象用 == 会出问题 .equals()
忘记更新栈状态 只判断未弹出,导致死循环 确保 pop() 正确调用
单调栈边界 栈空时的 peek/pop 操作 isEmpty() 判断
Deque 的 push/pop 与 addFirst/removeFirst 两套方法等价,但混用可能混淆 统一用 push/pop/peek 语义
场景 核心思想 经典题目
括号匹配 遇到左括号压栈,遇到右括号弹出匹配 LeetCode 20
表达式求值 操作数栈 + 运算符栈,处理优先级 LeetCode 150
单调栈 维护单调递增/递减栈,找下一个更大/更小元素 LeetCode 739, 42, 84
DFS(深度优先搜索) 用栈模拟递归,避免递归栈溢出 LeetCode 94
撤销操作(Undo) 每步操作压栈,撤销时弹出 系统设计题
浏览器前进后退 两个栈配合,前进栈 + 后退栈 系统设计题

自动装箱:

java 复制代码
// 自动装箱:int → Integer
Integer a = 100;  // 等价于 Integer.valueOf(100)

// 自动拆箱:Integer → int
int b = a;        // 等价于 a.intValue()

// 在集合中自动装箱
Deque<Integer> stack = new ArrayDeque<>();
stack.push(512);   // int 512 自动装箱成 Integer
int val = stack.pop();  // Integer 自动拆箱成 int

基本语句:

java 复制代码
import java.util.*;

public class StackDemo {
    public static void main(String[] args) {
        // 1. 创建栈
        Deque<Character> stack = new ArrayDeque<>();
        
        // 2. 压栈 push
        stack.push('a');
        stack.push('b');
        stack.push('c');
        System.out.println(stack);  // [a, b, c](栈顶在右)
        
        // 3. 查看栈顶 peek
        char top = stack.peek();
        System.out.println(top);    // c(不出栈)
        
        // 4. 弹栈 pop
        char popped = stack.pop();
        System.out.println(popped); // c
        System.out.println(stack);  // [a, b]
        
        // 5. 判空 isEmpty
        boolean empty = stack.isEmpty();
        System.out.println(empty);  // false
        
        // 6. 获取大小 size
        int size = stack.size();
        System.out.println(size);   // 2
        
        // 7. 清空栈(方法一:逐个弹出)
        while (!stack.isEmpty()) {
            stack.pop();
        }
        
        // 8. 清空栈(方法二:clear)
        // stack.clear();
        
        System.out.println(stack.isEmpty());  // true
    }
}

20-有效的括号

**思路:**遍历每个字符,先把左括号压入栈,到右括号时出栈进行对比。

**注意:遍历s用toCharArray()**来变成单字符数组;

判断右括号,要考虑特殊情况 即栈空

整体返回 不直接返true ,万一奇数个括号,因此返回**stack.isEmpty.()**是否为空

java 复制代码
class Solution {
    public boolean isValid(String s) {
        Deque <Character> stack=new ArrayDeque<>();
        //左括号压入栈,右括号匹配就出栈
        for(char c:s.toCharArray()){  //将s换成字符数组
            if(c=='('||c=='['||c=='{'){//判断左括号,入栈
                stack.push(c);
            }else{//判断右括号
                if(stack.isEmpty()){ //栈为空,即无对应左括号
                    return false;
                }
                char pop= stack.pop();//出栈判断是否左右匹配
                if(!isMatch(pop,c)){   
                    return false;
                }
            }
        }
        return stack.isEmpty();//栈为空,即偶数,则全部匹配成功
    }
    public boolean isMatch(char pop,char c){
        if(pop=='('&&c==')'||pop=='['&&c==']'||pop=='{'&&c=='}'){
            return true;
        }else{return false;}
    }
}

155-最小栈

**思路:按要求来,新建minstack来存历史最小值,最顶部就是当前最小值,只要更小就入minstack,所以其是越来越小。获取最小值直接minstack.peek()**就行

注意:int是披萨(值) ,Integer是披萨盒(地址),两者比较用.equals() 比较值,用**=**是判断两个地址是否相等,Deque等不接受int,只接受Integer盒子;

stack.pop()在if条件里就算执行了一次pop,不需要再写pop操作了;

如果没有最小值这个概念要求,直接 stack.pop(),就行了

java 复制代码
class MinStack {
    Deque <Integer>stack;
    Deque <Integer>minstack;
    public MinStack() {
        stack=new ArrayDeque<>();
        minstack=new ArrayDeque<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if(minstack.isEmpty()||val<=minstack.peek()){//minstack为空 或 <当前最小值
            minstack.push(val);
        }
    }
    
    public void pop() {
        
        if(stack.pop().equals(minstack.peek())){ //pop的与minstack顶部(当前最小值)相等时
            minstack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minstack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

394-字符串解码

思路:

遍历每个字符:

数字 → 累加 num

字母 → 拼到当前结果 curr

[ → 把 num 和 curr 压栈,然后重置 num=0, curr=""

] → 弹出 num 和 栈顶字符串,把 curr 重复 num 遍,再拼到栈顶字符串后面

**注意:**StringBuilder是可变字符串;

注意StringBuilder、String和char的格式,必须统一;

String是定长,StringBuilder是可变长;

num入栈出栈之后要置0;

需要使用temp来接住从栈里弹出来的字符串,方便重复操作,重复完变成当前值cur ;

java 复制代码
class Solution {
    public String decodeString(String s) {
        Deque <Integer> numStack=new ArrayDeque<>();//披萨盒子用<> 
        Deque <String> curStack=new ArrayDeque<>();//披萨盒子用<> 
        int num=0;
        StringBuilder cur=new StringBuilder(); //当前串,披萨不用<>
        for(char c:s.toCharArray()){//String要变成Char型
            if(c>='0'&&c<='9'){
                num=num*10+c-'0';
            }else if(c=='['){
                numStack.push(num);
                curStack.push(cur.toString()); //Char要变成String型
                num=0;
                cur=new StringBuilder();
            }else if(c>='a'&&c<='z'){
                cur.append(c);     //字母就拼接在cur上
            }else if(c==']'){
                num=numStack.pop();
                StringBuilder temp=new StringBuilder(curStack.pop());
                for(int i=0;i<num;i++){temp.append(cur);}//cur开始重复
                cur = temp; 
                num=0;
            }
        }
        return cur.toString();
    }
}

739-每日温度

**思路:**新建一个nums存每个位置的天数差,新建栈存天数;栈为空 入栈第一个;昨天出栈和今天比较,今天的温度大于昨天,则计算天数差,存入nums; 栈不为空,出栈操作持续,因为栈内都是未计算天数差的空值。

**注意:**出栈操作条件要判空

java 复制代码
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        //温度低入栈
        //温度高出栈 计算天数差
        int [] nums=new int[temperatures.length];
        Deque<Integer> stack=new ArrayDeque<>();
        for(int i=0;i<temperatures.length;i++){
            //栈不为空 且 比昨天温度高,就出栈
            while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
                int num=stack.pop(); //昨天
                nums[num]= i-num;  //计算天数差,i比较大,存入tem的结果里
            }
            stack.push(i);//比昨天温度低就入栈,对应nums表为空
        }
        return nums;
    }
}

84-柱状图最大矩形

思路: 求柱状图矩形面积,一般优先选最高的柱子来求;

遍历所有,目前值为 i 的高;

前一个>目前值 ,出栈, 更新h为前一个的,左右边界为两侧;

前一个<目前值,入栈;(特殊情况 :若一直高上坡,右边界的h设为0;若一直低下坡,则 左边界设为界外-1)

**注意:**考虑特殊情况1,当前值要判断是范围内还是最后一个;

考虑特殊情况2,一直出栈到边界,ok那设置左边界为-1;

java 复制代码
class Solution {
    public int largestRectangleArea(int[] heights) {
        Deque<Integer> stack=new ArrayDeque<>();
        int maxarea=0;
        int area=0;
        //出栈判断与之前栈顶大小,小则左边界向左走,大则入栈,一直大下去结尾放个0的哨兵
        for(int i=0;i<=heights.length;i++){
            int curHei=(i==heights.length)?0:heights[i];//当前高度,多设置哨兵0在最后,以防一直上坡
            while(!stack.isEmpty()&&curHei<heights[stack.peek()]){
                int h=heights[stack.pop()];//上一个实际高度,取出栈顶索引
                //当栈空,即左边都比当前大,边界设为-1;左边界为出站后,新栈的索引
                int left=stack.isEmpty()?-1:stack.peek();
                int right=i;//右边界是当前值
                area=(right-left-1)*h; 
                if(area>maxarea){maxarea=area;}//取最大面积
            }
            stack.push(i);
        }
        return maxarea;
    }
}
相关推荐
8Qi810 小时前
LeetCode 235. 二叉搜索树的最近公共祖先(LCA)
算法·leetcode·二叉树·递归·二叉搜索树·lca·迭代
bIo7lyA8v11 小时前
算法稳定性分析中的随机扰动建模的技术8
算法
sugar__salt11 小时前
从栈队列数据结构到JS原型面向对象全解
前端·javascript·数据结构
科研online11 小时前
基于多源数据和XGBoost-SHAP分析中国大陆绿地碳汇空间变异影响因素的非线性相关性与尺度差异
算法·学习方法
Cthy_hy11 小时前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
三品吉他手会点灯12 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
心之伊始12 小时前
Java 后端接入大模型:从 Token、并发到推理成本的完整估算方法
java·spring boot·性能优化·大模型·llm
VkN2X2X4b12 小时前
算法复杂度的实验验证与误差分析的技术8
算法
其利天下技术12 小时前
风扇灯无刷电机自适应算法实战指南
算法·cocos2d·无刷电机自适应算法·bldc驱动自适应算法·其利无刷电机驱动算法
8Qi812 小时前
LeetCode 494:目标和(Target Sum)—— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包