单调栈(算法)

单调栈的定义与类型

单调栈是一种栈结构,内部元素保持严格的单调递增或单调递减顺序。

分为两种基本类型:

  • 单调递增栈:栈底到栈顶元素逐渐增大
  • 单调递减栈:栈底到栈顶元素逐渐减小

核心原理与典型应用

在遍历数组时,利用栈来记录尚未找到"下一个更大/更小"元素的下标。当遇到一个破坏单调性的元素时,就依次弹出栈内元素,并当场结算这些弹出元素的答案。

主要解决两类问题:

  • 寻找数组中每个元素 左边/右边 第一个比它 大/小 的元素。

  • 计算特定几何结构的极值(如凹槽面积、矩形面积)

基础代码模板

右侧第一个更大元素

java 复制代码
public int[] nextGreaterElement(int[] nums) {
    int n = nums.length;
    int[] res = new int[n];
    Deque<Integer> stack = new ArrayDeque<>();
    
    for (int i = 0; i < n; i++) {
        while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
            int idx = stack.pop();
            res[idx] = nums[i];
        }
        stack.push(i);
    }
    while (!stack.isEmpty()) {
        res[stack.pop()] = -1;
    }
    return res;
}

右侧第一个更小元素 仅需修改比较符号:

java 复制代码
while (!stack.isEmpty() && nums[i] < nums[stack.peek()]) {
    // 处理逻辑
}

经典问题解析

接雨水问题(LeetCode 42)

思路 :按列求雨水。使用单调递减栈(栈底最大),当 height[i] > height[stack.peek()] 时形成凹槽:

  1. 弹出 bottom 作为底部。

  2. 若栈空则无法蓄水,否则 left = stack.peek() 为左边界。

  3. 宽度 = i - left - 1

  4. 高度 = min(height[left], height[i]) - height[bottom]

java 复制代码
public int trap(int[] height) {
    Deque<Integer> stack = new ArrayDeque<>();
    int res = 0;
    for (int i = 0; i < height.length; i++) {
        while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
            int bottom = stack.pop();
            if (stack.isEmpty()) break;
            int left = stack.peek();
            int w = i - left - 1;
            int h = Math.min(height[left], height[i]) - height[bottom];
            res += w * h;
        }
        stack.push(i);
    }
    return res;
}

最大矩形问题(LeetCode 84)

思路 :寻找每个柱子左右两边第一个比它矮的柱子,宽度 = right - left - 1,高度 = heights[i]

使用单调递增栈(栈底最小),当 heights[i] < heights[stack.peek()] 时触发

java 复制代码
public int largestRectangleArea(int[] heights) {
    int n = heights.length;
    int[] newHeights = new int[n + 2]; // 哨兵技巧,避免越界
    System.arraycopy(heights, 0, newHeights, 1, n);
    Deque<Integer> stack = new ArrayDeque<>();
    int res = 0;
    for (int i = 0; i < newHeights.length; i++) {
        while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {
            int h = newHeights[stack.pop()];
            int left = stack.peek();
            int w = i - left - 1;
            res = Math.max(res, h * w);
        }
        stack.push(i);
    }
    return res;
}

时间复杂度与空间复杂度

  • 时间复杂度:O(n),每个元素仅入栈出栈一次
  • 空间复杂度:O(n),栈的存储开销

关键注意事项

  1. 下标存储优于值存储,便于计算宽度
  2. 比较条件选择(严格单调/非严格单调)需根据题意调整
  3. 推荐使用ArrayDeque代替传统Stack
相关推荐
圆山猫6 分钟前
[Virtualization](四):Linux KVM/RISC-V 的 vCPU 运行路径
java·linux·risc-v
城管不管42 分钟前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
似的83543 分钟前
一步一步学习使用FireMonkey动画() 使用TAnimator类创建动画
linux·学习·nginx
IT小白杨1 小时前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器
月疯1 小时前
二分法算法(水平等分图形面积)
算法
tq10861 小时前
开放与封闭:类型系统的安全哲学
笔记
fanged1 小时前
总线学习3--SPI番外(TODO)
学习
豆瓣鸡1 小时前
算法日记 - Day3
java·开发语言·算法
白白白小纯1 小时前
算法篇—反转链表
c语言·数据结构·算法·leetcode
萧瑟余晖2 小时前
Java深入解析篇九之NIO详解
java·网络·nio