1. 方法一:代码比较多
若一个位置的高度为
h
1
h_1
h1,在栈内,那么就说明后面有可能出现高为
h
1
h_1
h1而宽度更大的矩形,如果不在栈内,那么就说明后面不可能出现高为
h
1
h_1
h1而宽度更大的矩形。
可以通过以下方式判断有没有可能出现高为
h
1
h_1
h1而宽度更大的矩形:
如果
h
1
h_1
h1右边的高度
h
2
h_2
h2比
h
1
h_1
h1要小,那么右边界就卡死了,高
h
1
h_1
h1的矩形的宽度就不能更大了,高
h
1
h_1
h1的最大面积矩形已经确定,就可以将
h
1
h_1
h1弹出栈,同时计算面积。
然而如果出现了
h
1
>
h
2
h_1>h_2
h1>h2这种右边比左边小的情况,不仅能判断有没有可能出现高为
h
1
h_1
h1而宽度更大的矩形,还能判断是否能出现高为
h
(
h
2
<
h
<
h
1
)
h(h_2 < h< h_1)
h(h2<h<h1)的宽度更大矩形。因为
h
2
h_2
h2将右边界卡死了,所以
h
h
h高度的在栈内的高度矩形最大面积也可确定了。就可以将这些也弹出栈,同时计算面积。
当全部
h
(
h
2
<
h
<
h
1
)
h(h_2 < h< h_1)
h(h2<h<h1)索引弹出栈后,可以将
h
2
h_2
h2索引压入栈。
接下来介绍如何求已经不能更宽的矩形面积,也就是高度为弹出的高度
h
(
h
2
<
h
<
h
1
)
h(h_2 < h< h_1)
h(h2<h<h1)的最宽矩形面积。最大面积为
h
×
(
右
边
界
−
左
边
界
)
h\times(右边界-左边界)
h×(右边界−左边界),其中
右
边
界
=
h
2
索
引
−
1
右边界=h_2索引-1
右边界=h2索引−1,
左
边
界
=
弹
出
h
后
栈
顶
索
引
+
1
左边界=弹出h后栈顶索引+1
左边界=弹出h后栈顶索引+1。因为这样维护出的栈是一个递增栈,所以弹出
h
h
h后,栈顶的高度一定小于等于
h
h
h,也就是左边界卡死了。
然而需要了解到,高度数组最右边的高度需要特殊处理,因为其没有右边高度了。处理方式是如果扫到最右边的高度,则默认按照该高度右边有一个高度更低的高度来处理,因为不可能再出现宽度更大的矩形了。
还需要了解到,在考察左边界的时候,栈可能为空,这时候就可以将0作为左边界。因为当前矩形的高度一定左边的任何高度都要低,不然不会最后一个弹出栈。
完整代码:
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# 特殊情况判断
n = len(heights)
if n == 0:
return 0
if n == 1:
return heights[0]
result = 0
stack = [0]
for i in range(1, n):
right = i-1
# 右边界卡死
while stack and heights[i] < heights[stack[-1]]:
h1 = heights[stack.pop()]
# 左边界
left = 0
if stack:
left = stack[-1]+1
result = max(result, (right-left+1)*h1)
stack.append(i)
right = n-1
# 处理最右边高度
while stack:
h1 = heights[stack.pop()]
# 左边界
left = 0
if stack:
left = stack[-1]+1
result = max(result, (right-left+1)*h1)
return result
2. 方法二:方法一进阶版,加上了“哨兵”,使得代码更简洁
在方法1中,因为边界问题,处理了两种特殊情况。所以在高度数组的最开始位置和最后位置分别加上一个0高度元素当作哨兵,这样栈就不会为空,且高度数组中的最后一个元素也能一起处理,就不用处理特殊情况了。
完整代码:
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
# 特殊情况判断
n = len(heights)
if n == 0:
return 0
if n == 1:
return heights[0]
# 最左哨兵
heights.insert(0,0)
# 最右哨兵
heights.append(0)
n += 2
result = 0
stack = [0]
for i in range(1, n):
right = i-1
# 右边界卡死
while heights[i] < heights[stack[-1]]:
h1 = heights[stack.pop()]
# 左边界
left = stack[-1]+1
result = max(result, (right-left+1)*h1)
stack.append(i)
return result
最后不死心的附上一份超时的O( n 2 n^2 n2)暴力代码。过不了,别试了。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
result = 0
n = len(heights)
if n == 0:
return 0
minNum = [[0 for i in range(n)] for j in range(n)]
for i in range(n):
minNum[i][i] = heights[i]
tmpMax = minNum[i][i]
for j in range(i+1, n):
minNum[i][j] = min(minNum[i][j-1], heights[j])
tmpMax = max(tmpMax, minNum[i][j]*(j-i+1))
result = max(result, tmpMax)
# 如果当前区间最小数乘上从i到n的长度,都比当前的最优解要小,那么剪枝
if minNum[i][j]*(n-i+1) < result:
break
result = max(result, tmpMax)
return result