单调栈(算法)

单调栈的定义与类型

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

分为两种基本类型:

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

核心原理与典型应用

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

主要解决两类问题:

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

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

基础代码模板

右侧第一个更大元素

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 分钟前
Atebrioz(zilurgisertib):靶向ALK2通路,FOP超罕见病全新口服治疗方案
笔记
不要打扰75613 分钟前
《房地产经纪操作实务》重点和难点是什么?用哪个题库好
经验分享·职场和发展
源码宝17 分钟前
云HIS系统源码,HIS全套源码,支持监管系统对接与二次开发
java·源码·电子病历·云his系统·程序代码·全套源码
和侧18 分钟前
MOS器件——ESD测试
笔记·学习
OpenDataLab20 分钟前
Agent 如何免费接入多个文献数据库?一个好用的工具分享
经验分享
优化Henry20 分钟前
光路异常告警排查实战:LTE室分与5G小区两个案例解析
运维·网络·学习·5g·tdd
萧瑟余晖21 分钟前
Spring MVC拦截器异常处理与跨域详解
java·spring·mvc
泡茶喝茶写代码31 分钟前
A股量化数据工程:从 REST 接口到策略信号(第 17 篇):创新高与新低扫描
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
All for pursuit.32 分钟前
【动态规划-6】96.不同的二叉搜索树
数据结构·c++·算法·leetcode·动态规划
栗子~~41 分钟前
java - logstash-logback-encoder 集成 demo
java·开发语言·logback