小哆啦解题记:守护最小值的魔法栈

155. 最小栈 - 力扣(LeetCode)

🎬 第一幕:小哆啦的栈塔挑战

力扣大陆的天空,悬浮着一座高耸的"栈塔"。塔门口,一行火红的文字闪烁:

"想进塔顶,先回答这个问题:

设计一个能 随时告诉我最小值 的栈,所有操作必须 O(1) ,包括 pushpoptopgetMin。"

小哆啦眼睛一亮:

"这不就是普通栈 + Math.min 吗?小菜一碟!"

于是他写下了第一版代码:

typescript 复制代码
class MinStack {
    private stack: number[] = [];
    push(val: number) { this.stack.push(val); }
    pop() { this.stack.pop(); }
    top() { return this.stack[this.stack.length - 1]; }
    getMin() { return Math.min(...this.stack); } // 看,多优雅!
}

他兴冲冲按下运行按钮,力扣精灵冷冷吐出一句话:

"getMin() 时间复杂度 O(n),你确定敢带这代码进塔?"

小哆啦吓了一跳:

"哎呀,我每次 Math.min 都要遍历一遍栈,确实慢到怀疑人生。"

他第一次意识到:

  • 栈操作可以 O(1),但 getMin 每次 O(n),整体性能差到爆炸。
  • 如果有百万次 getMin,程序直接凉凉。

🧠 第二幕:为什么 O(1) 难住了小哆啦?

小哆啦在树下沉思:

"pushpop 好办,但 getMin 怎么做到 O(1) 呢?

如果能提前记住最小值就好了,可问题是------最小值可能会被 pop 掉!"

哆啦A梦递过一本秘籍,封面写着:
《辅助栈:记录历史最低点》

秘籍核心思想:

每次 push,一个栈存当前值,另一个栈同步记录当前最小值的"快照"。
pop 时两个栈一起退,保证最小值信息不会乱。

举个例子:
push 5 → minStack = [5]
push 3 → minStack = [5, 3]
push 7 → minStack = [5, 3, 3]

为什么第三次还是 3?因为 7 比 3 大,最小值还是 3,所以复制一遍最小值


⚔️ 第三幕:魔法代码诞生

kotlin 复制代码
class MinStack {
    private stack: number[] = [];
    private minStack: number[] = [];

    push(val: number): void {
        this.stack.push(val);
        const minVal = this.minStack.length === 0 ? val : Math.min(val, this.minStack[this.minStack.length - 1]);
        this.minStack.push(minVal);
    }

    pop(): void {
        this.stack.pop();
        this.minStack.pop();
    }

    top(): number {
        return this.stack[this.stack.length - 1];
    }

    getMin(): number {
        return this.minStack[this.minStack.length - 1];
    }
}

每次 push,记录历史最小值快照

getMin → 直接取辅助栈末尾,O(1)

pop → 两个栈同步退位,状态永远一致


🏆 第四幕:空间换时间的哲学

小哆啦写下心得:

  • 以前:每次 getMin 扫一遍 → 时间爆炸
  • 现在:每次 push 多存一个数 → 空间略大,但性能飞升
  • 典型的"空间换时间",就像哆啦A梦的口袋,随手一个道具换取无限可能。

🧩 第五幕:进阶优化------如何压缩空间?

突然,胖虎冷笑:

"你这辅助栈太胖了!能不能减肥?"

小哆啦灵机一动:

"其实没必要在 minStack 复制所有最小值,只在遇到 更小值 时才 push,pop 时如果 top 是当前最小值,再 pop 一次。"

优化版代码:

kotlin 复制代码
class MinStack {
    private stack: number[] = [];
    private minStack: number[] = [];

    push(val: number): void {
        this.stack.push(val);
        if (this.minStack.length === 0 || val <= this.minStack[this.minStack.length - 1]) {
            this.minStack.push(val);
        }
    }

    pop(): void {
        const val = this.stack.pop();
        if (val === this.minStack[this.minStack.length - 1]) {
            this.minStack.pop();
        }
    }

    top(): number {
        return this.stack[this.stack.length - 1];
    }

    getMin(): number {
        return this.minStack[this.minStack.length - 1];
    }
}

✅ 空间优化成功:minStack 只存最小值的历史节点

✅ 仍然保持 O(1) 查询

✅ 压缩内存,胖虎终于满意


🎯 栈 VS 辅助栈对比总结

方法 getMin复杂度 空间 可维护性
暴力遍历 O(n) O(1) 简单但低效
辅助栈 O(1) O(n) 稳定快速
空间压缩版 O(1) O(k) (k ≪ n) 进阶最优

🏁 终章:哲学彩蛋

小哆啦仰望栈塔:

"原来,辅助栈就像人生的备份系统,帮你记住最低谷的每一步。

只有记住自己的'最小值',你才能在关键时刻快速反应。"

相关推荐
workflower7 小时前
用硬件换时间”与“用算法降成本”之间的博弈
人工智能·算法·安全·集成测试·无人机·ai编程
重生之我是Java开发战士9 小时前
【动态规划】简单多状态dp问题:按摩师,打家劫舍,删除并获得点数,粉刷房子,买卖股票的最佳时机
算法·动态规划·哈希算法
KAU的云实验台10 小时前
单/多UAV、静/动态路径规划,基于PlatEMO平台的带约束多目标优化 本文核心内容:
算法·matlab·无人机
Liangwei Lin10 小时前
洛谷 P1807 最长路
数据结构·算法
会编程的土豆10 小时前
【数据结构与算法】二叉树从建立开始
数据结构·c++·算法
_日拱一卒10 小时前
LeetCode:最大子数组和
数据结构·算法·leetcode
计算机安禾10 小时前
【数据结构与算法】第22篇:线索二叉树(Threaded Binary Tree)
c语言·开发语言·数据结构·学习·算法·链表·visual studio code
算法鑫探11 小时前
解密2025数字密码:数位统计之谜
c语言·数据结构·算法·新人首发
计算机安禾11 小时前
【数据结构与算法】第21篇:二叉树遍历的经典问题:由遍历序列重构二叉树
c语言·数据结构·学习·算法·重构·visual studio code·visual studio