84.柱状图中最大的矩形
和接雨水题目类似,但细节处却有不同。同样有三种解法,暴力法,双指针法,单调栈法。
暴力解法
求以哪根柱子的高度得到的矩形最大。循环遍历每根柱子,然后向左遍历得到柱子左边第一个小于柱子高度的,向右遍历得到柱子右边第一个小于柱子高度的,两者之间的距离为矩形的宽度,当前柱子的高度为矩形的高度。
代码超时
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
result = 0
for i in range(len(heights)):
left = -1
right = len(heights)
# 寻找左边第一个小于当前柱子的
for j in range(i-1, -1, -1):
if heights[j] < heights[i]:
left = j
break
# 寻找右边第一个小于当前柱子的
for j in range(i+1, len(heights)):
if heights[j] < heights[i]:
right = j
break
w = right - left - 1
result = max(result, heights[i] * w)
return result
双指针解法
建立在暴力解法之上,优化了求当前柱子左右两边第一个小于柱子高度的过程,其中利用了已经求解的数据,不再是暴力遍历,具体可以看代码
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# 存储当前柱子左右两边第一个小的柱子索引
min_left = [-1] * len(heights)
min_right = [len(heights)] * len(heights)
for i in range(1, len(heights)):
t = i - 1
while t >= 0 and heights[t] >= heights[i]:
t = min_left[t]
min_left[i] = t
for i in range(len(heights)-2, -1, -1):
t = i + 1
while t < len(heights) and heights[t] >= heights[i]:
t = min_right[t]
min_right[i] = t
result = 0
for i in range(len(heights)):
w = min_right[i] - min_left[i] - 1
result = max(result, heights[i] * w)
return result
单调栈解法
遇见比栈顶元素小的元素开始弹出,栈顶元素的高度为矩形的高度,(当前元素索引 - 栈第二个元素索引 - 1) 为矩形的宽度。
这道题和接雨水的不同之处是雨水在第一个和最后一个是不可能堆积的,并且开头递增和结尾递减也是不可能接到雨水的;
而矩形是可以在第一个和最后一个形成的,因此我们需要在height数组的前后各插一个0元素,简化代码实现同时能够使栈内所有正常数据弹出,最后栈内只会保留两个0元素
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights.insert(0, 0)
heights.append(0)
stack = []
result = 0
for i in range(len(heights)):
while stack and heights[i] < heights[stack[-1]]:
mid = stack.pop()
left = stack[-1]
w = i - left - 1
h = heights[mid]
result = max(result, w * h)
stack.append(i)
return result