【码道初阶】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(),除非你明确知道自己在比较内存地址!

相关推荐
小飞Coding2 小时前
你写的 equals() 和 hashCode(),正在悄悄吃掉你的数据!
java·后端
dragoooon342 小时前
[C++——lesson26.「多态」]
java·c++·学习方法·多态
我送炭你添花2 小时前
Pelco KBD300A 模拟器:05.校验算法终极对比 + 完整 100+ 指令封装 + KBD300A 所有隐藏功能函数化
python·算法·自动化·运维开发
计算机学姐2 小时前
基于SSM的网上花店销售系统【2026最新】
java·vue.js·mysql·java-ee·tomcat·intellij-idea·mybatis
DuHz2 小时前
汽车FMCW雷达互扰下的快速目标检测:谱峰累积法与泊松CFAR精读与推导
论文阅读·算法·目标检测·汽车·信息与通信·信号处理
2401_837088502 小时前
算法边界情况处理套路总结
算法
.墨迹.2 小时前
汇总笔试题
java
悟空码字3 小时前
Java短信验证码保卫战,当羊毛党遇上“铁公鸡”
java·后端
爱吃KFC的大肥羊3 小时前
Redis 基础完全指南:从全局命令到五大数据结构
java·开发语言·数据库·c++·redis·后端