这两道题目都是经典的单调栈应用场景,属于数据结构与算法中较有挑战性的问题。接雨水问题最早由美国计算机科学家Donald Knuth在《计算机程序设计艺术》中提出,而柱状图最大矩形问题则是LeetCode高频面试题之一。它们共同考察了以下几个核心能力:
在实际工程中,这类问题的解决思路可以应用于:
最直观的暴力解法是对每个位置向左右扫描寻找最高柱子,时间复杂度O(n²)。通过预处理左右最大值数组可以优化到O(n):
python复制def trap(height):
n = len(height)
left_max = [0] * n
right_max = [0] * n
left_max[0] = height[0]
for i in range(1, n):
left_max[i] = max(left_max[i-1], height[i])
right_max[-1] = height[-1]
for i in range(n-2, -1, -1):
right_max[i] = max(right_max[i+1], height[i])
res = 0
for i in range(n):
res += min(left_max[i], right_max[i]) - height[i]
return res
单调栈解法更考验对问题本质的理解。我们维护一个单调递减栈,当遇到比栈顶高的柱子时,说明可能形成凹槽:
python复制def trap(height):
stack = []
res = 0
for i in range(len(height)):
while stack and height[i] > height[stack[-1]]:
bottom = height[stack.pop()]
if not stack:
break
left = stack[-1]
distance = i - left - 1
bounded_height = min(height[left], height[i]) - bottom
res += distance * bounded_height
stack.append(i)
return res
关键理解点:栈内存储的是索引而非高度,这样既能比较高度又能计算距离。每次计算的是当前柱子与栈顶前一个柱子形成的横向水槽。
枚举所有可能的矩形需要O(n²)时间复杂度,这在n较大时(如10^5量级)完全不可行。我们需要找到每个柱子能扩展的最大宽度。
维护一个单调递增栈,当遇到比栈顶小的柱子时,说明找到了右边界:
python复制def largestRectangleArea(heights):
stack = [-1]
max_area = 0
heights.append(0) # 哨兵值
for i in range(len(heights)):
while stack[-1] != -1 and heights[stack[-1]] > heights[i]:
h = heights[stack.pop()]
w = i - stack[-1] - 1
max_area = max(max_area, h * w)
stack.append(i)
return max_area
技术细节:添加哨兵值0保证最后所有柱子都能被处理。宽度计算是当前索引减去新的栈顶索引再减1,这个需要画图理解。
通过这两道题可以总结出单调栈的通用解法框架:
通用代码结构:
python复制def monotonic_stack(nums):
stack = []
res = 0
for i in range(len(nums)):
while stack and nums[i] >/< nums[stack[-1]]:
# 弹出栈顶并计算
top = stack.pop()
# 具体计算逻辑
stack.append(i)
# 处理剩余元素
return res
调试案例:
输入:[0,1,0,2,1,0,1,3,2,1,2,1]
逐步打印栈状态和水量计算过程:
i=0: stack=[0]
i=1: 弹出0,栈空 → stack=[1]
i=2: stack=[1,2]
i=3: 弹出2,计算水量(1,3)→1; 弹出1→ stack=[3]
...
可视化调试方法:
画出柱状图,标注每次弹出时的矩形范围,用不同颜色标记已计算区域。
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 接雨水-双指针 | O(n) | O(1) | 内存敏感 |
| 接雨水-单调栈 | O(n) | O(n) | 需要过程可视化 |
| 最大矩形-单调栈 | O(n) | O(n) | 必须使用 |
空间优化技巧:
某地理信息系统需要计算山区地形在暴雨后的积水区域。将地形高程数据作为输入数组,使用接雨水算法可以:
金融分析系统中,需要识别K线图中的关键支撑/阻力位。改进后的算法可以:
给定二维矩阵表示的高度图,计算能接多少雨水。这需要将单调栈扩展到二维情况,典型解法:
在矩阵中寻找全为1的最大矩形,这是动态规划经典问题。可以使用:
在面试场景中遇到这类问题时:
常见面试问题:
python复制def trap(height):
l, r = 0, len(height)-1
left_max = right_max = res = 0
while l < r:
if height[l] < height[r]:
left_max = max(left_max, height[l])
res += left_max - height[l]
l += 1
else:
right_max = max(right_max, height[r])
res += right_max - height[r]
r -= 1
return res
python复制def largestRectangleArea(heights):
n = len(heights)
left = [0] * n
right = [n] * n
stack = []
for i in range(n):
while stack and heights[stack[-1]] >= heights[i]:
right[stack[-1]] = i
stack.pop()
left[i] = stack[-1] if stack else -1
stack.append(i)
return max(h * (right[i] - left[i] - 1) for i, h in enumerate(heights))
有效的测试用例应该包含:
示例测试集:
python复制test_cases = [
([], 0), # 空数组
([2], 0), # 单柱子
([0,0,0,0], 0), # 平地
([1,0,1], 1), # 简单凹槽
([5,4,3,2,1], 0), # 严格递减
([1,2,3,4,5], 9) # 严格递增
]
对于单调栈问题,建议:
调试工具推荐:
单调栈的思想最早可以追溯到1972年Knuth提出的"栈排序"问题。现代算法竞赛中:
近年来的发展:
巩固练习题目:
这些题目都使用了单调栈的变种,可以帮助深化理解。建议按照从易到难的顺序练习,每道题至少尝试两种不同的实现方式。