这两道算法题是经典的空间计算类问题,在技术面试中出现频率极高。接雨水问题考察对空间关系的三维想象能力,柱状图最大矩形则考验对单调栈的灵活运用。我在刷题过程中发现,很多解法的代码虽然简洁,但背后的思维过程却鲜有详细讲解。这里我将拆解这两个问题的思考路径,分享从暴力解法到最优解的完整优化过程。
给定n个非负整数表示的高度图,计算按此排列的柱子能接多少雨水。示例输入:[0,1,0,2,1,0,1,3,2,1,2,1],直观来看就像凹凸不平的地面形成的积水区域。
关键观察:每个柱子上方的积水量由它左右两侧最高柱子的较小值决定,这就是著名的"木桶短板效应"。
最直接的思路是对每个柱子,向左右扫描找到最高柱子:
python复制def trap_brute(height):
res = 0
for i in range(1, len(height)-1):
left_max = max(height[:i])
right_max = max(height[i+1:])
res += max(0, min(left_max, right_max) - height[i])
return res
时间复杂度O(n²),在LeetCode上会超时。主要问题在于重复计算左右最大值。
预处理左右最大值数组:
python复制def trap_dp(height):
if not height: return 0
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
时间复杂度O(n),空间复杂度O(n)。这是典型的空间换时间策略。
通过左右指针相向移动,实时维护当前左右最大值:
python复制def trap(height):
left, right = 0, len(height)-1
left_max = right_max = res = 0
while left < right:
if height[left] < height[right]:
left_max = max(left_max, height[left])
res += left_max - height[left]
left += 1
else:
right_max = max(right_max, height[right])
res += right_max - height[right]
right -= 1
return res
空间复杂度优化到O(1),是面试官最期待的解法。关键在于理解为什么可以单边计算水量。
给定n个非负整数表示的柱状图高度,求能勾勒出的最大矩形面积。示例输入:[2,1,5,6,2,3],最大面积为10(柱子[5,6]形成的区域)。
暴力解法尝试所有可能的左右边界:
python复制def largestRectangleArea_brute(heights):
max_area = 0
n = len(heights)
for i in range(n):
min_height = float('inf')
for j in range(i, n):
min_height = min(min_height, heights[j])
max_area = max(max_area, min_height * (j-i+1))
return max_area
O(n²)时间复杂度无法通过大数据测试。
维护一个高度递增的栈,当遇到较小值时触发面积计算:
python复制def largestRectangleArea(heights):
stack = []
heights = [0] + heights + [0]
res = 0
for i in range(len(heights)):
while stack and heights[stack[-1]] > heights[i]:
h = heights[stack.pop()]
w = i - stack[-1] - 1
res = max(res, h * w)
stack.append(i)
return res
这个解法有三大关键点:
时间复杂度O(n),每个元素入栈出栈各一次。空间复杂度O(n)。类似问题还有:
从暴力解法到最优解通常经历:
LeetCode 407题将问题扩展到三维空间,需要结合优先队列按高度分层处理。
在01矩阵中寻找全1子矩阵(LeetCode 85),可以逐行构建高度数组后调用柱状图解法。
我在面试中遇到的一个变种是:给定柱状图和一个宽度k,求所有连续k个柱子组成的最大矩形面积。这需要在滑动窗口中维护单调队列。