单调栈与堆的经典应用:柱状图最大矩形 & 数组第 K 大元素深度解析

目录

[一、柱状图中最大的矩形(LeetCode 84・困难)](#一、柱状图中最大的矩形(LeetCode 84・困难))

题目描述

解题思路

核心思想

[Java 代码实现(标准单调栈版)](#Java 代码实现(标准单调栈版))

复杂度分析

核心知识点总结

[二、数组中的第 K 个最大元素(LeetCode 215・中等)](#二、数组中的第 K 个最大元素(LeetCode 215・中等))

题目描述

解题思路

核心思想(小顶堆)

[Java 代码实现(小顶堆版)](#Java 代码实现(小顶堆版))

复杂度分析

核心知识点总结

三、两道题的核心对比与联系

核心联系


今天我们来拆解两道算法面试高频题:柱状图中最大的矩形 (单调栈难题)和数组中的第 K 个最大元素 (堆的经典应用)。这两道题分别代表了单调栈两大核心数据结构的标杆应用,吃透它们能帮你彻底掌握这两种结构的灵活使用,是算法面试的必考点。


一、柱状图中最大的矩形(LeetCode 84・困难)

题目描述

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例:

plaintext

复制代码
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

输入:heights = [2,4]
输出:4

解题思路

这道题的核心是找到每个柱子能扩展的最大左右边界 ,从而计算以该柱子为高的最大矩形面积。暴力法时间复杂度为 O(n2),而单调栈可以将时间复杂度优化到 O(n)。

核心思想
  1. 维护一个单调递增栈:栈中存储柱子的下标,保证栈中下标对应的高度始终递增。
  2. 遍历数组,找左右边界
    • 对于当前柱子 i,若栈顶柱子高度 heights[stack.peek()] >= heights[i],说明栈顶柱子的右边界 就是 i
    • 弹出栈顶下标 top,计算以 heights[top] 为高的最大矩形面积:
      • 左边界为新的栈顶下标 left = stack.peek()(若栈空则为 -1
      • 宽度为 i - left - 1
      • 面积为 heights[top] * 宽度
    • 若栈顶高度小于当前高度,将 i 压入栈中。
  3. 处理栈中剩余元素 :遍历结束后,栈中剩余柱子的右边界为数组长度 n,重复上述计算过程。
  4. 最终返回最大面积

Java 代码实现(标准单调栈版)

java

运行

复制代码
import java.util.Stack;

public class LargestRectangleInHistogram {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int maxArea = 0;
        // 栈中存储柱子下标,维护单调递增
        Stack<Integer> stack = new Stack<>();

        for (int i = 0; i <= n; i++) {
            // 用0作为哨兵,处理栈中剩余元素
            int currentHeight = (i == n) ? 0 : heights[i];
            // 当前高度小于栈顶高度,弹出并计算面积
            while (!stack.isEmpty() && heights[stack.peek()] >= currentHeight) {
                int top = stack.pop();
                int height = heights[top];
                // 左边界:栈空则为-1,否则为栈顶下标
                int left = stack.isEmpty() ? -1 : stack.peek();
                // 宽度 = 右边界(i) - 左边界(left) - 1
                int width = i - left - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }

        return maxArea;
    }
}

复杂度分析

  • 时间复杂度:O(n),每个下标入栈 / 出栈最多一次,总操作次数为 O(n)
  • 空间复杂度:O(n),最坏情况(严格递增)栈的大小为 O(n)

核心知识点总结

  1. 单调递增栈的作用:快速找到每个柱子的左右边界,避免暴力遍历
  2. 哨兵优化 :在数组末尾补 0,统一处理栈中剩余元素,简化代码逻辑
  3. 边界计算 :左边界为栈顶下标(栈空则为 -1),右边界为当前下标,宽度为 右-左-1

二、数组中的第 K 个最大元素(LeetCode 215・中等)

题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例:

plaintext

复制代码
输入: [3,2,1,5,6,4], k = 2
输出: 5

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

解题思路

这道题有两种经典解法:小顶堆 (时间复杂度 O(nlogk))和快速选择 (时间复杂度 O(n),平均情况),这里重点讲解面试最常用的小顶堆解法。

核心思想(小顶堆)
  1. 维护一个大小为 k 的小顶堆 :堆顶始终是当前堆中的最小值(即前 k 大元素中的最小值)。
  2. 遍历数组
    • 若堆大小小于 k,直接将元素入堆;
    • 若堆大小等于 k,比较当前元素与堆顶:
      • 若当前元素大于堆顶,弹出堆顶,将当前元素入堆;
      • 否则跳过。
  3. 遍历结束后,堆顶元素即为第 k 大元素

Java 代码实现(小顶堆版)

java

运行

复制代码
import java.util.PriorityQueue;

public class KthLargestElementInArray {
    public int findKthLargest(int[] nums, int k) {
        // 小顶堆,默认自然排序
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();

        for (int num : nums) {
            if (minHeap.size() < k) {
                minHeap.offer(num);
            } else if (num > minHeap.peek()) {
                minHeap.poll();
                minHeap.offer(num);
            }
        }

        return minHeap.peek();
    }
}

复杂度分析

  • 时间复杂度:O(nlogk),每个元素入堆 / 出堆的时间复杂度为 O(logk),遍历数组时间为 O(n)
  • 空间复杂度 :O(k),堆的大小为 k

核心知识点总结

  1. 小顶堆的优势:空间复杂度低(仅需 O(k)),适合处理海量数据(如流式数据)
  2. 堆的选择 :求第 k 大用小顶堆,求第 k 小用大顶堆
  3. 快速选择拓展:基于快速排序的分治思想,平均时间复杂度 O(n),最坏 O(n2),适合面试加分

三、两道题的核心对比与联系

表格

题目 核心目标 数据结构 核心逻辑
柱状图中最大的矩形 找每个柱子的最大左右边界,计算最大面积 单调递增栈 维护栈的单调性,快速定位边界,优化时间复杂度
数组中的第 K 个最大元素 找数组中第 K 大的元素 小顶堆 维护大小为 k 的小顶堆,堆顶即为第 K 大元素

核心联系

两道题分别代表了单调栈两大核心数据结构的经典应用:

  • 单调栈:解决「找左右边界、下一个更大 / 更小元素」等序列问题,时间复杂度 O(n)
  • :解决「TopK、优先级队列」等问题,时间复杂度 O(nlogk),空间复杂度低
相关推荐
宸津-代码粉碎机1 天前
Spring AI 企业级RAG实战|增量更新+文档去重+定时自动入库生产落地方案
java·大数据·人工智能·后端·python·spring
正在走向自律1 天前
告别低效繁琐!DeepSeek+Python 重塑科研绘图新范式
python·开发工具·deepseek·ai辅助编程
Evand J1 天前
【代码介绍】自适应R的AEKF(自适应扩展卡尔曼滤波)和经典EKF比较,MATLAB例程|三维非线性系统
开发语言·matlab·ekf·自适应·自适应滤波
曾阿伦1 天前
Unicode 正则表达式开发指南
python·正则表达式
香辣西红柿炒蛋1 天前
yaml文件介绍、数据读取
python
乐于分享的阿乐1 天前
(二)VSCode搭建python环境(详细图文保姆级教程)
ide·vscode·python
雪的季节1 天前
1 个网络线程 + 3 个数据处理线程(完全隔离)
开发语言
weixin_408099671 天前
2026 AI生成图片快速去水印的5种实测方法(附在线工具 + Python/Java/PHP API代码)
java·人工智能·python·api接口·ai去水印·石榴智能·自动去水印
风筝在晴天搁浅1 天前
快手 CodeTop LeetCode 227.基本计算器Ⅱ
java·开发语言
2601_961194021 天前
2026初级会计经济法基础知识点汇总
python·django·pdf·virtualenv·代理模式·pygame