单调栈(算法)

单调栈的定义与类型

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

分为两种基本类型:

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

核心原理与典型应用

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

主要解决两类问题:

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

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

基础代码模板

右侧第一个更大元素

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
相关推荐
sali-tec42 分钟前
C# 基于OpenCv的视觉工作流-章108-区域筛选
图像处理·人工智能·opencv·算法·计算机视觉
Gigavision6 小时前
基于BUAA-MIHR数据集的噪声解耦对比学习算法
人工智能·python·深度学习·算法
鹿角片ljp6 小时前
LeetCode 64:最小路径和复盘|二维 DP 与 ACM 模式完整写法
java·数据结构·算法
彧azz8 小时前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
泡海椒8 小时前
PDF 表格样式优化:jquick-pdf 边框、圆角、背景色
java·开发语言·pdf
Beyond_System|系统之外8 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
景熙55239 小时前
15.Java 8 Stream 流入门到实战
java·开发语言·数据结构
wno7049 小时前
Spring Security权限控制
java·python·spring
一条小小yu9 小时前
基于数据库唯一索引 + 过期时间的分布式锁
学习