第七周算法作业 No.85,152
85. 最大矩形
-
思路
此题基于第84题,所以先讲解84题的思路,再回到本题
84题的链接为: https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
-
84题
采用的方法是单调栈
整体思路是对每个柱子求解该柱子能够容纳的最大矩形,需要分别计算左边第一个比当前柱子矮的下标以及右边第一个比当前柱子矮的下标。
-
例子
我们用一个具体的例子
[6, 7, 5, 2, 4, 5, 9, 3]
来理解单调栈。我们需要求出每一根柱子的左侧且最近的小于其高度的柱子。初始时的栈为空。我们枚举 6,因为栈为空,所以 6 左侧的柱子是「哨兵」,位置为 -1。随后我们将 6 入栈。
栈:
[6(0)]
。(这里括号内的数字表示柱子在原数组中的位置)
我们枚举 7,由于6<7
,因此不会移除栈顶元素,所以 7 左侧的柱子是 6,位置为 0。随后我们将 7 入栈。栈:
[6(0), 7(1)]
我们枚举 5,由于7≥5
,因此移除栈顶元素 7。同样地,6≥5
,再移除栈顶元素 6。此时栈为空,所以 5左侧的柱子是「哨兵」,位置为 −1。随后我们将 5 入栈。栈:
[5(2)]
接下来的枚举过程也大同小异。我们枚举 2,移除栈顶元素 5,得到 2 左侧的柱子是「哨兵」,位置为 −1。将 2 入栈。栈:
[2(3)]
我们枚举 4,5 和 9,都不会移除任何栈顶元素,得到它们左侧的柱子分别是 2,4 和 5,位置分别为 3,4 和 5。将它们入栈。栈:
[2(3), 4(4), 5(5), 9(6)]
我们枚举 3,依次移除栈顶元素 9,5 和 4,得到 3 左侧的柱子是 2,位置为 3。将 3入栈。栈:
[2(3), 3(7)]
这样以来,我们得到它们左侧的柱子编号分别为[-1, 0, -1, -1, 3, 4, 5, 3]
。用相同的方法,我们从右向左进行遍历,也可以得到它们右侧的柱子编号分别为[2, 2, 3, 8, 7, 7, 7, 8]
,这里我们将位置 8 看作「哨兵」。在得到了左右两侧的柱子之后,我们就可以计算出每根柱子对应的左右边界,并求出答案了。
-
回到85题
观看下图,可以看先其实逐行处理与84题处理方式一样,分别计算每个柱子的高度,然后调用84题的单调栈进行最大矩形面积的计算。
- 代码
class Solution(object):
def maximalRectangle(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
if len(matrix)==0:
return 0
rows = len(matrix)
cols = len(matrix[0])
# 参考84题,逐层寻找最大面积的长方形
# 使用[]模拟栈,栈里面的数字单调递增
result =0
for i in range(rows):
# 使用heights记录每列柱子的高度
heights = [0]*cols
for j in range(cols):
h = i
while h >=0 and matrix[h][j]=='1':
h = h-1
heights[j] = i-h
'''
print(i, 'cols')
for j in range(len(heights)):
print(heights[j])
'''
result = max(result, self.largestRectangleArea(heights))
return result
def largestRectangleArea(self, heights):
"""
:type heights: List[int]
:rtype: int
"""
result = 0
# leftMin用于记录每个柱子左侧比该柱子低的第一个柱子的位置
leftMin = [-1]*len(heights)
# rightMin用于记录每个柱子右侧比该柱子低的第一个柱子的位置
rightMin = [len(heights)]*len(heights)
# 使用单调栈,python中用[]来代替,里面装载下标
# 先把最左边的推入栈
stack = [0]
for i in range(1,len(heights)):
# 如果当前的高度比栈顶元素大,则入栈
# 当前元素的leftMin为栈顶元素的位置
if heights[i]> heights[stack[len(stack)-1]]:
leftMin[i] = stack[len(stack)-1]
stack.append(i)
else:
# 当前的元素无法直接入栈
# 需要先将栈里面比当前元素大的元素处理完
# 再把当前元素入栈
while len(stack)>0 and \
heights[i]<=heights[stack[len(stack)-1]] :
stack.pop()
if len(stack)>0:
leftMin[i] = stack[len(stack)-1]
stack.append(i)
# 如果原先栈中的所有元素都被pop出来
# 说明没有元素比当前的元素小,则leftMin[i] = -1,初始化时已处理
# 同样的处理逻辑处理rightMin
stack = [len(heights)-1]
for i in range(len(heights)-2, -1, -1):
if heights[i]> heights[stack[len(stack)-1]]:
rightMin[i] = stack[len(stack)-1]
stack.append(i)
else:
while len(stack)>0 and \
heights[i]<=heights[stack[len(stack)-1]] :
stack.pop()
if len(stack)>0:
rightMin[i] = stack[len(stack)-1]
stack.append(i)
# 计算每个柱子所能勾勒的最大矩形的面积
for i in range(len(heights)):
#print(i,leftMin[i],rightMin[i])
result = max(result, heights[i]*(rightMin[i]-leftMin[i]-1))
return result
- 作业截图
152. 乘积最大子数组
-
思路
使用动态规划,dpMin[i]表示包含i的最小乘积,dpMax[i]表示包含i的最大乘积,由于存在正负数,因此对于正负数而言,最大最小值的转移状态不同。
当当前元素为正数,则转移方程为:dpMax = max( dpMax*nums[i], nums[i] ) dpMin = min( dpMin*nums[i], nums[i] )
当当前元素为负数,则转移方程为:
dpMax = max( dpMin*nums[i], nums[i] ) dpMin = min( dpMax*nums[i], nums[i] )
-
代码
class Solution(object):
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = nums[0]
# 使用动态规划
# dpMin[i]表示包含i的最小乘积
# dpMax[i]表示包含i的最大乘积
dpMax = nums[0]
dpMin = nums[0]
for i in range(1, len(nums)):
if nums[i]>0:
dpMax = max( dpMax*nums[i], nums[i] )
dpMin = min( dpMin*nums[i], nums[i] )
elif nums[i]<0:
dpMaxTmp = max( dpMin*nums[i], nums[i] )
dpMin = min( dpMax*nums[i], nums[i] )
dpMax = dpMaxTmp
else:
# nums[i]==0
dpMax = 0
dpMin = 0
#print('i',i,'dpMax',dpMax,'dpMin' ,dpMin)
result = max(result, dpMax)
return result
- 作业截图