题目链接 | 解题思路
本题和接雨水的题目相互呼应,但是难度略有提升,同样是一道非常棒的题!
在接雨水中,需要找到每一列的左侧最大值和右侧最大值;在本题中,需要找到每一列的左侧第一个更小值和右侧第一个更小值。这个要求的变化加大了双指针的难度,所以优先讨论更加适用的单调栈解法。
和上一题一样按行来计算矩形面积,对于固定的一列,需要这一列的高度、对应的左边界、对应的右边界。但这一题,单调栈的储存顺序发生了变化。
对于当前元素与栈口元素的三种比较情况,也和接雨水中的方法无异。
本题最大的不同就在于需要在输入数组的首尾各加入一个 0,从而解决一些特殊的输入数组:
单调递增数组:heights = [2, 4, 6, 8]
单调递减数组:heights = [8, 6, 4, 2]
可以看到,在接雨水中我们没有特殊考虑过这两种情况。因为在接雨水中,如果不能够形成一个完整的谷,那么就不需要进行任何计算,也就是说单调递增、递减的数组本来就不该有任何计算结果。而在本题,任何输入都应该正确计算面积,所以不能只计算完整的峰,而要全面考虑。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights.insert(0, 0)
heights.append(0)
result = 0
stack = [0]
for i in range(1, len(heights)):
if heights[i] > heights[stack[-1]]:
stack.append(i)
elif heights[i] == heights[stack[-1]]:
stack.pop()
stack.append(i)
else:
while (len(stack) > 0 and heights[i] < heights[stack[-1]]):
mid = stack[-1]
stack.pop()
if len(stack) > 0:
left = stack[-1]
curr_width = i - left - 1
curr_height = heights[mid]
result = max(result, curr_height * curr_width)
stack.append(i)
return result
和接雨水一样,本题同样有双指针 + dp 的解法,但是这个解法的时间复杂度有些可疑。
和接雨水相比,本题中的双指针 + dp 会更加复杂一些,因为要求的不再是最大值而是第一个更小值的下标,dp 的递推变得很困难,我也不确定具体的复杂度。
这题的 dp 部分有点像是 KMP,感觉很有意思。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# records the index of the first smaller value on the left of i
left_smaller_idx = [0] * len(heights)
left_smaller_idx[0] = -1 # no smaller value on the left, representing non-existence
for i in range(1, len(heights)):
temp = i - 1
while (temp >= 0 and heights[temp] >= heights[i]):
temp = left_smaller_idx[temp]
left_smaller_idx[i] = temp
# records the index of the first smaller value on the right of i
right_smaller_idx = [0] * len(heights)
right_smaller_idx[-1] = len(heights) # no smaller value on the right, representing non-existence
for i in range(len(heights) - 2, -1, -1):
temp = i + 1
while (temp < len(heights) and heights[temp] >= heights[i]):
temp = right_smaller_idx[temp]
right_smaller_idx[i] = temp
result = 0
for i in range(len(heights)):
result = max(result, heights[i] * (right_smaller_idx[i] - left_smaller_idx[i] - 1))
return result