news 2026/4/17 16:24:18

LeetCode刷题必备:用单调栈5分钟搞定‘直方图最大矩形’和‘子数组最值差’

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode刷题必备:用单调栈5分钟搞定‘直方图最大矩形’和‘子数组最值差’

LeetCode刷题必备:用单调栈5分钟搞定‘直方图最大矩形’和‘子数组最值差’

在算法面试中,单调栈是一种极其高效的数据结构,尤其适用于解决那些看似复杂但实则模式固定的问题。本文将深入探讨如何利用单调栈快速解决LeetCode中的两个经典问题:直方图最大矩形(LeetCode 84)和子数组最值差(LeetCode 907)。通过掌握这些技巧,你可以在面试中迅速识别问题模式并给出最优解。

1. 单调栈的核心思想与应用场景

单调栈是一种特殊的栈结构,它保持栈内元素的单调性(递增或递减)。这种特性使得它在处理"下一个更大元素"或"前一个更小元素"类问题时表现出色。

单调栈的典型应用场景包括:

  • 寻找数组中每个元素的下一个更大/更小元素
  • 计算直方图中的最大矩形面积
  • 求解子数组的最大值/最小值问题
  • 处理滑动窗口极值问题

提示:单调栈的时间复杂度通常是O(n),因为它确保每个元素只入栈和出栈一次

2. 直方图最大矩形问题(LeetCode 84)

这个问题要求我们在给定的直方图中找出面积最大的矩形。单调栈提供了一种优雅的解决方案。

2.1 解题思路拆解

  1. 初始化:创建一个空栈,并在直方图末尾添加一个高度为0的虚拟柱子
  2. 遍历柱子
    • 当当前柱子高度小于栈顶柱子高度时,开始计算面积
    • 弹出栈顶元素,计算以该柱子为高的最大矩形面积
  3. 更新结果:比较并记录最大面积值

2.2 代码模板实现

def largestRectangleArea(heights): stack = [] heights = heights + [0] # 添加虚拟柱子 max_area = 0 for i, h in enumerate(heights): while stack and heights[stack[-1]] > h: height = heights[stack.pop()] width = i if not stack else i - stack[-1] - 1 max_area = max(max_area, height * width) stack.append(i) return max_area

关键点解析:

  • 虚拟柱子的添加确保了所有柱子都会被处理
  • width的计算需要考虑栈为空的情况
  • 时间复杂度:O(n),空间复杂度:O(n)

3. 子数组最值差问题(LeetCode 907)

这个问题要求计算所有子数组的最大值与最小值之差的总和。单调栈同样能高效解决。

3.1 解题思路拆解

  1. 分解问题:总和 = 所有子数组最大值之和 - 所有子数组最小值之和
  2. 使用单调栈
    • 计算每个元素作为最大值能覆盖的区间范围
    • 计算每个元素作为最小值能覆盖的区间范围
  3. 组合结果:根据覆盖范围计算贡献值并求和

3.2 代码模板实现

def sumSubarrayMins(arr): MOD = 10**9 + 7 n = len(arr) # 计算每个元素作为最小值的影响范围 left = [0] * n stack = [] for i in range(n): while stack and arr[stack[-1]] > arr[i]: stack.pop() left[i] = stack[-1] if stack else -1 stack.append(i) # 计算每个元素作为最大值的影响范围 right = [0] * n stack = [] for i in range(n-1, -1, -1): while stack and arr[stack[-1]] >= arr[i]: stack.pop() right[i] = stack[-1] if stack else n stack.append(i) # 计算最小值贡献总和 min_sum = 0 for i in range(n): min_sum += arr[i] * (i - left[i]) * (right[i] - i) # 计算最大值贡献总和(类似方法) # ... return min_sum % MOD

优化技巧:

  • 可以一次性计算最大值和最小值的影响范围
  • 使用数学方法直接计算贡献值而非枚举所有子数组
  • 注意处理相等元素时的边界条件

4. 单调栈的通用解题模板

基于上述两个问题,我们可以总结出单调栈的通用解题模板:

4.1 单调递增栈模板

stack = [] for i in range(n): while stack and nums[stack[-1]] > nums[i]: # 处理弹出的元素 top = stack.pop() # 计算相关值 stack.append(i)

4.2 单调递减栈模板

stack = [] for i in range(n): while stack and nums[stack[-1]] < nums[i]: # 处理弹出的元素 top = stack.pop() # 计算相关值 stack.append(i)

应用场景对比表:

问题类型适用栈类型关键处理步骤
下一个更大元素单调递减栈弹出时记录当前元素是栈顶元素的下一个更大元素
直方图最大矩形单调递增栈弹出时计算以栈顶元素为高的矩形面积
子数组最值差两者结合分别计算作为最大值和最小值的影响范围

5. 实战调试技巧与常见错误

在实际编码中,单调栈问题有几个常见的陷阱需要注意:

  1. 边界处理

    • 栈为空时的特殊处理
    • 数组首尾元素的处理
    • 相等元素的处理(严格单调还是非严格单调)
  2. 索引计算

    • 宽度计算是否正确
    • 影响范围是否包含边界
  3. 复杂度分析

    • 确保每个元素只被处理一次
    • 避免双重循环导致的O(n²)复杂度

注意:在LeetCode 907中,处理最大值和最小值时要注意相等元素的处理方式,通常一个方向用严格比较,另一个方向用非严格比较,以避免重复计算或遗漏

6. 扩展应用与变种问题

掌握了单调栈的基本应用后,可以尝试解决以下变种问题:

  1. 滑动窗口最大值(LeetCode 239)
  2. 接雨水问题(LeetCode 42)
  3. 股票跨度问题(LeetCode 901)
  4. 最大矩形问题(LeetCode 85)

这些问题都可以通过适当调整单调栈的用法来解决。例如,在解决"接雨水"问题时,可以使用单调栈来跟踪可能形成"凹槽"的柱子,从而计算积水量。

在实际面试中,遇到涉及"区间极值"、"下一个更大/更小元素"或"面积计算"的问题时,应该首先考虑单调栈是否适用。通过大量练习,可以培养出对这类问题的敏锐直觉,从而在面试中快速识别并解决它们。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 16:23:44

掌握Windows系统优化:Winhance中文版构建高效定制化工作流

掌握Windows系统优化&#xff1a;Winhance中文版构建高效定制化工作流 【免费下载链接】Winhance-zh_CN A Chinese version of Winhance. C# application designed to optimize and customize your Windows experience. 项目地址: https://gitcode.com/gh_mirrors/wi/Winhanc…

作者头像 李华
网站建设 2026/4/17 16:22:42

C语言分支和循环语句

if语句/else语句if&#xff08;条件&#xff09;{操作}else{操作}注意三目操作符逻辑操作符可以嵌套eg&#xff1a;if&#xff08;&#xff08;ab&&bc)||a>c){操作}短路但是不管真假都会对左边第一个进行计算执行switch语句case和default顺序可以任意调换while循环f…

作者头像 李华