单调栈——从“找下一个更大的“到柱状图中的最大矩形

本文是「数据结构与算法面试精讲」系列第21篇。前20篇覆盖了链表、二叉树、回溯、DP、哈希表、BST、滑动窗口、栈队列、二分查找、双指针、排序、图、前缀和、堆、Trie、并查集、LRU Cache等全部常见数据结构和算法。第21篇,我们来聊一个"栈的进阶用法"------单调栈。它不是一个全新的数据结构,而是一种"模式",一旦掌握,就能解决一大类问题。


一、单调栈基本原理

单调栈的核心思想是:维护一个栈,栈内元素保持单调递增或单调递减,在遍历过程中,当遇到破坏单调性的元素时,触发"结算"操作。

单调递增栈 vs 单调递减栈

类型 栈内顺序 解决的问题 典型题目
单调递减栈 栈底到栈顶递减 找下一个更大元素 739. 每日温度
单调递增栈 栈底到栈顶递增 找下一个更小元素 84. 柱状图最大矩形

核心模板

复制代码
// 单调递减栈------找下一个更大元素
for (int i = 0; i < nums.length; i++) {
    while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
        int idx = stack.pop();
        // 结算:nums[i] 是 nums[idx] 的下一个更大元素
    }
    stack.push(i);
}

时间复杂度

  • 每个元素入栈一次、出栈最多一次

  • O(n),比暴力法的 O(n²) 大幅优化


二、739. 每日温度(Easy)

问题描述

给定一个温度数组,返回需要等多少天才会出现更高的温度。

复制代码
输入: temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
输出: [1, 1, 4, 2, 1, 1, 0, 0]

Java实现

复制代码
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] result = new int[n];
        Deque<Integer> stack = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                int idx = stack.pop();
                result[idx] = i - idx;
            }
            stack.push(i);
        }
        return result;
    }
}

Python实现

复制代码
class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        n = len(temperatures)
        result = [0] * n
        stack = []
        for i in range(n):
            while stack and temperatures[i] > temperatures[stack[-1]]:
                idx = stack.pop()
                result[idx] = i - idx
            stack.append(i)
        return result

C++实现

复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> result(n, 0);
        stack<int> st;
        for (int i = 0; i < n; i++) {
            while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
                int idx = st.top(); st.pop();
                result[idx] = i - idx;
            }
            st.push(i);
        }
        return result;
    }
};

三、84. 柱状图中最大的矩形(Hard)

问题描述

给定一个数组,表示柱状图中每个柱子的高度,找出最大矩形面积。

复制代码
输入: heights = [2, 1, 5, 6, 2, 3]
输出: 10  (由高度5和6组成的5x2矩形)

思路推导

核心洞察:对每个柱子,以它的高度为高,向左向右找到第一个比它矮的柱子,左右边界之间的宽度就是它的最大宽度。

用单调递增栈:遍历时,当当前柱子高度小于栈顶,说明栈顶的右边界找到了,开始结算。

Java实现(哨兵优化)

复制代码
class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] h = new int[n + 2];
        System.arraycopy(heights, 0, h, 1, n);
        h[0] = 0;
        h[n + 1] = 0;

        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(0);
        int maxArea = 0;

        for (int i = 1; i <= n + 1; i++) {
            while (h[i] < h[stack.peek()]) {
                int height = h[stack.pop()];
                int width = i - stack.peek() - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }
        return maxArea;
    }
}

Python实现

复制代码
class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        h = [0] + heights + [0]
        n = len(h)
        stack = [0]
        max_area = 0
        for i in range(1, n):
            while h[i] < h[stack[-1]]:
                height = h[stack.pop()]
                width = i - stack[-1] - 1
                max_area = max(max_area, height * width)
            stack.append(i)
        return max_area

四、单调栈问题分类

题目 难度 类型 核心思路
739. 每日温度 Easy 下一个更大元素 单调递减栈
496. 下一个更大元素 I Easy 下一个更大元素 单调递减栈 + HashMap
503. 下一个更大元素 II Medium 下一个更大元素(循环数组) 单调递减栈 + 遍历两轮
84. 柱状图中最大矩形 Hard 最大面积 单调递增栈 + 哨兵
85. 最大矩形 Hard 最大面积(二维) 逐行化为84题
42. 接雨水 Hard 面积计算 单调栈/双指针

五、总结

单调栈的核心公式

复制代码
暴力法 O(n²) + 单调栈优化 = O(n)

判断什么时候用单调栈

  1. 问题涉及"找下一个更大/更小元素"

  2. 暴力法需要两层循环

  3. 内层循环的"终止条件"具有单调性

面试答题顺序

  1. 先讲暴力法,分析为什么慢

  2. 引入单调栈,画图演示

  3. 写代码实现

  4. 分析复杂度


参考资料

  1. LeetCode 739. Daily Temperatures --- 官方题解

  2. LeetCode 84. Largest Rectangle in Histogram --- 官方题解

  3. 《算法竞赛进阶指南》------单调栈章节

相关推荐
GoppViper1 小时前
RDF资源描述框架深度解析:语义Web的数据基石与实战逻辑
前端·数据库
小灰灰搞电子1 小时前
C++ 引用折叠详解
c++
2601_962174171 小时前
Redis 简介
数据库·redis·wpf
夜雪一千1 小时前
MySQL 各种数据同步方案全梳理:从简单复制到异构同步实战
数据库·mysql
靠沿2 小时前
贪心算法专题(二)
算法·贪心算法
布莱克6052 小时前
数组详解:定义、作用、应用场景及与链表的区别(C/C++ 代码讲解)
c语言·开发语言·c++·数组
猎头南楼2 小时前
具身智能模型部署方向的能力要求与技术栈梳理
人工智能·深度学习·算法
Highcharts.js3 小时前
Highcharts 下钻(Drilldown)柱状图 Demo
数据库·信息可视化·hbase·highcharts·drilldown·js模块·下钻图表
牛油果子哥q3 小时前
C++内存池与对象池精讲:内存碎片、自定义分配器、对象池实现、STL allocator原理、工程落地与性能对比
java·开发语言·c++