单调栈(算法)

单调栈的定义与类型

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

分为两种基本类型:

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

核心原理与典型应用

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

主要解决两类问题:

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

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

基础代码模板

右侧第一个更大元素

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
相关推荐
黑夜里的小夜莺1 小时前
黑马点评登录成功后点击【我的】会跳转到登录页面 BUG 修复
java·bug
可爱の小公举1 小时前
Redis面试高频考点全解析
人工智能·学习·职场和发展·ai编程
lightqjx1 小时前
【前端】前端学习三之初识JavaScript
前端·javascript·学习
程序阿北1 小时前
Karpathy 的知识库构想被人做成桌面应用了,而且做得相当扎实,已在 Github 上斩获 5.8k+ Star!
经验分享
诙_2 小时前
C++数据结构--排序算法
数据结构·算法·排序算法
wuyikeer2 小时前
Java进阶——IO 流
java·开发语言·python
jieyucx2 小时前
Go 切片核心:子切片详解(下篇)
开发语言·算法·golang·切片
●VON2 小时前
猫咪专注 CatFocus 技术博客:一款鸿蒙原生自律计时工具的设计与实现
学习·华为·harmonyos·von·猫咪专注
游乐码2 小时前
c#特性笔记
笔记·c#