← 中文资源

简体中文版本

常数时间获取最小值的栈

通过并行的最小值栈,在常数时间内获取数值栈当前最小值,无需每次扫描全部元素。

阅读英文原文

功能说明

MinStack 在普通后进先出栈的基础上支持 O(1) 的 min 查询。每次 push 时同步记录到当前位置为止的最小值,pop 时两个栈一起弹出,因此栈顶的最小值始终对应当前元素范围。适合滑动窗口、股票价格、撤销历史以及需要实时最小值的编辑器状态等场景。peek、pop、size 与 toArray 提供常规栈操作。

关键代码

内部使用两个原生数组,values 保存元素,minimums 保存前缀最小值。push 时取当前最小值与新区比较,空栈时以 Infinity 作为初始值,保证第一个元素成为最小值。pop 同时弹出两个数组,空栈返回 undefined 而不抛错。min 在空栈时返回 undefined,因为数组末位不存在。元素必须是有限数字,NaN 被拒绝以避免比较失效。

使用注意

最小值栈占用与元素数量相同的额外空间,若数据量极大且只需偶尔查询,可改用稀疏记录或分段最小值。Infinity 只是内部哨兵,不会被返回,除非显式 push 了 Infinity。该结构不支持删除中间元素,遇到需要撤销任意位置的操作应换用其他数据结构。pop 的 undefined 返回值与真实 undefined 元素无法区分,但类型校验已禁止 undefined 入栈。

验证方式

依次 push 3、1、2,断言 min 为 1,pop 后 min 仍为 1,再 pop 后 min 为 3。测试重复最小值、递增序列、递减序列和空栈的 peek、pop、min。验证 size 随操作变化、toArray 不暴露内部数组。最后测试字符串、NaN、undefined 入栈会抛错,并确认链式 push 返回实例本身。

代码示例

export class MinStack {
  #values = [];
  #minimums = [];

  push(value) {
    if (typeof value !== "number" || Number.isNaN(value)) {
      throw new TypeError("value must be a number");
    }
    this.#values.push(value);
    const currentMin = this.#minimums.length > 0
      ? this.#minimums[this.#minimums.length - 1]
      : Infinity;
    this.#minimums.push(Math.min(value, currentMin));
    return this;
  }

  pop() {
    if (this.#values.length === 0) return undefined;
    this.#minimums.pop();
    return this.#values.pop();
  }

  peek() {
    return this.#values[this.#values.length - 1];
  }

  min() {
    return this.#minimums[this.#minimums.length - 1];
  }

  get size() {
    return this.#values.length;
  }

  toArray() {
    return [...this.#values];
  }
}