单调栈(算法)

单调栈的定义与类型

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

分为两种基本类型:

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

核心原理与典型应用

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

主要解决两类问题:

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

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

基础代码模板

右侧第一个更大元素

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
相关推荐
JieE2128 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack2016 小时前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树18 小时前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
唐青枫21 小时前
Java JDBC 实战指南:从 Connection 到事务和连接池
java
一个做软件开发的牛马1 天前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
用户3721574261351 天前
Java 处理 PDF 图片:提取 PDF 中的图片,并压缩 PDF 图片体积
java
用户3721574261351 天前
Java 打印 Word 文档:从基础打印到高级设置
java
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法