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

相关推荐
cheems9527几秒前
[SpringMVC] Spring MVC 留言板开发实战
java·spring·mvc
BioRunYiXue2 分钟前
AlphaGenome:DeepMind 新作,基因组学迎来 Alpha 时刻
java·linux·运维·网络·数据库·人工智能·eclipse
游乐码6 分钟前
C#Dicitionary
算法·c#
whatever who cares7 分钟前
android中,全局管理数据/固定数据要不要放一起?
android·java·开发语言
华清远见IT开放实验室11 分钟前
AI 算法核心知识清单(深度实战版1)
人工智能·python·深度学习·学习·算法·机器学习·ai
牧瀬クリスだ12 分钟前
七大排序一次满足
数据结构·算法·排序算法
liu****12 分钟前
第15届省赛蓝桥杯大赛C/C++大学B组
开发语言·数据结构·c++·算法·蓝桥杯·acm
C1829818257513 分钟前
AI idea 集成claude code插件
java·ide·intellij-idea
IT 行者13 分钟前
解决 IntelliJ IDEA 内存占用高的两个优化策略:GPU 渲染与虚拟内存配置
java·ide·intellij-idea·ai编程
Aric_Jones15 分钟前
从实战理解异步、并发并行与GIL:FastAPI vs SpringBoot
java·spring boot·fastapi