题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
题目来源
Loading...leetcode-cn.com解题思路:使用栈的数据结构解答该题目
调试环境:python3 + MacOS
难点:python里面栈的数据结构可以很方便使用list的pop和append方法来实现压栈和出栈,也就是先进后出的数据结构。此题目的难点在于把索引压栈之后如何去求解局部最大长方形面积。
推导思路(非严格数学证明):
(1)长方形的面积为长✖️高,此题目中长为索引值差值,比如求n个相邻柱子长方形长为n索引值 减去第一个柱子索引值,而高是最低柱子值决定的。所以局部面积最大 area=(index_n-index_m)* lowest_highest。把索引值存入一个stack的数据结构中,这样可以方便求宽度。
(2) 假设数组为height=[2,3,4]先考虑升序情况求局部最大值,通过画图推导很容易得出*升序情况下局部最大值公式(stack[-1]-index) * height[index]
我们可以得到升序求最大面积代码:
def _increase_list(self, heights: list) -> int:
# [1, 3, 4, 7]
stack_index = [index for index in range(len(heights))]
max_area = 0
top_index = len(stack_index)
while stack_index:
index = stack_index.pop()
width = top_index - index
area = heights[index] * width
print(area)
max_area = max(max_area, area)
return max_area(3) 最关键一步来了,如果将出现降序怎么办呢?根据第一步我们知道局部最大是由最低的柱子决定的,所以我们不必急着算出局部升序所有面积值,利用栈的性质,弹出末尾最大值,然后比较是否任然升序,这样保证我们始终构造了一个升序的模型,比如柱子高度为[1,2,4,3]时候,图画的有点丑~
说明:当3进入之后我们发现比最高的4更小,这个时候我们弹出4,入栈3,我们发现由于索引值是增长的,所以并不影响高为2的时候求最大面积,公式和之前方式基本一致,这个是我觉得这个算法最tricky的地方,如果第一次接触此类题目,很难想到。也是因为这一步,避免了暴力方法频繁计算面积,大大降低计算时间。
理清楚思路之后,我们再整理代码。
class Solution2:
def largestRectangleArea(self, heights: list) -> int:
"""
:type heights: List[int]
:rtype: int
"""
heights.append(-1) # 末尾添加一个-1,方便我们用索引值相减,求出长方形的宽。假设最后一个柱子为最大的单个柱子,索引值为index,则宽度为len(height)-index
max_area = 0
stack_index = []
# 索引值入栈
index = 0
while index < len(heights):
if stack_index: # 如果不为空,判断是否升序,如果升序,则入栈
if heights[index] >= heights[stack_index[-1]]:
stack_index.append(index)
index = index + 1
else: # 否则开始计算,直到升序
previous_index = stack_index.pop()
if stack_index: # 如果索引栈不为空,则用当前索引减去栈定索引值得到宽
max_area = max(max_area, (index - stack_index[-1] - 1) * heights[previous_index])
# 注意:如果没有高度为0的柱子求宽度的表达式index - previous_index成立,如果有则有问题
# max_area = max(max_area, (index - previous_index) * heights[previous_index])
else: # 如果索引栈已经为空,则用当前索引值最为宽,比如传入值为[3,2,1]这种情况,因为已经为空,说明之前的柱子都更高
# 如果有0高度的柱子,必定在最前面,此时高度为零,不影响计算
max_area = max(max_area, index * heights[previous_index])
else: # 如果为空,则入栈
stack_index.append(index)
index = index + 1
return max_area需要注意一点是再求宽度时候刚开始使用表达式index - previous_index,如果没有高度为零的柱子是没有问题的,如果出现高度为零的柱子,则有问题。
代码地址:
Danielyan86/LeetcodePython3github.com