【码道初阶】Leetcode155踩坑最小栈问题:最小栈:算法对了,却输给了 Java 的 “==“?

【LeetCode 踩坑】最小栈:算法对了,却输给了 Java 的 "=="?

在做 LeetCode 155. 最小栈 时,我使用经典的"双栈法":一个栈 stack 存数据,另一个栈 minstack 存当前的最小值。

逻辑非常通顺:

  1. Push :如果新来的数小于等于 minstack 栈顶,同步压入 minstack
  2. Pop :如果要弹出的数等于 minstack 栈顶,minstack 也要同步弹出。

代码写得行云流水,前面的测试用例也都过了,结果在提交时在第 25 个用例翻车了:
输入push(512), push(-1024)...
预期getMin() 返回 512
实际getMin() 返回 -1024

为什么前面的小数字(如 1, 2, 3)都没事,一旦出现 512 或 -1024 这种大数就出错了?

1. 案发现场

问题出在 pop() 操作的判断逻辑上:

java 复制代码
public void pop() {
    // ... 省略空判断
    
    // 💀【致命错误】这里使用了 == 来比较两个 Integer 对象
    if(stack.peek() == minstack.peek()) 
    {
        stack.pop();
        minstack.pop();
        return;
    }
    stack.pop();
}

2. 深度解析:Integer 的"骗局"

在 Java 中,泛型栈 Stack<Integer> 里存的不是基本数据类型 int,而是包装类 Integer 对象。

当我们写 stack.peek() == minstack.peek() 时,我们实际上是在比较两个对象的内存地址

为什么小数字能通过?

Java 为了节省内存,设计了一个 Integer Cache(整数缓存池) 。默认范围是 -128 到 127

  • 当你 push(100) 时,Java 从缓存池里拿了一个对象给你。
  • 两个栈里的 100 指向的是同一个内存地址
  • 此时 obj1 == obj2 返回 true。逻辑正常执行。

为什么大数字(512, -1024)报错?

一旦数值超出了缓存范围(比如 -1024):

  • Java 会 new Integer(-1024)
  • stack 里的 -1024 是对象 A。
  • minstack 里的 -1024 是对象 B。
  • 虽然它们的值一样,但 地址 A != 地址 B
  • 判定条件 == 返回 false

后果

代码认为要弹出的不是最小值,于是只执行了 stack.pop(),保留了 minstack 里的值。

导致 minstack 里残留了一个本该被删除的最小值(-1024),当栈里只剩下 512 时,getMin() 依然错误的返回了这个幽灵值 -1024。

3. 避坑指南

修正方案 A:使用 .equals()(推荐)

这是比较对象数值的标准做法。

java 复制代码
// 修正后
if(stack.peek().equals(minstack.peek())) {
    stack.pop();
    minstack.pop();
} else {
    stack.pop();
}

修正方案 B:拆箱比较

强制转换成 int 基本类型,触发自动拆箱,这样 == 比较的就是数值了。

java 复制代码
// 虽然可行,但写法略繁琐
if(stack.peek().intValue() == minstack.peek().intValue())

4. 优化后的完整代码

除了修复 Bug,我还稍微优化了一下 pop 的逻辑结构,使其更简洁:

java 复制代码
class MinStack {
    // 使用 Deque 替代 Stack 是更现代的 Java 写法,不过 Stack 也没问题
    Stack<Integer> stack;
    Stack<Integer> minStack;

    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        // 注意这里要有等于号,处理重复最小值的情况
        if(minStack.isEmpty() || val <= minStack.peek()) {
            minStack.push(val);
        }
    }
    
    public void pop() {
        if(stack.isEmpty()) return;
        
        // 1. 先弹出数据栈的元素,拿到它的值
        Integer popVal = stack.pop();
        
        // 2. 检查这个值是否等于最小栈顶
        // ⚠️ 使用 .equals() 避免大数判错
        if(!minStack.isEmpty() && popVal.equals(minStack.peek())) {
            minStack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

5. 总结

这道题其实是披着算法皮的语言基础题

  • 算法层面:考察辅助栈的思想(空间换时间)。
  • 语言层面 :考察对 Java 包装类、对象比较 (== vs .equals) 以及 Integer 缓存机制的理解。

记住一句话:在 Java 中比较对象的值,永远首选 .equals(),除非你明确知道自己在比较内存地址!

相关推荐
2501_9418656311 分钟前
从事件驱动到异步架构的互联网工程语法构建与多语言实践分享
java·开发语言·jvm
全靠bug跑7 小时前
Spring Cache 实战:核心注解详解与缓存过期时间配置
java·redis·springcache
Swift社区7 小时前
LeetCode 465 最优账单平衡
算法·leetcode·职场和发展
聆风吟º7 小时前
【数据结构手札】空间复杂度详解:概念 | 习题
java·数据结构·算法
weixin_445054727 小时前
力扣热题51
c++·python·算法·leetcode
计算机程序设计小李同学7 小时前
基于SpringBoot的个性化穿搭推荐及交流平台
java·spring boot·后端
是一个Bug7 小时前
50道核心JVM面试题
java·开发语言·面试
地平线开发者7 小时前
linux 常见稳定性问题分析方法
算法·自动驾驶
朱朱没烦恼yeye7 小时前
java基础学习
java·python·学习
s砚山s7 小时前
代码随想录刷题——二叉树篇(九)
算法