数组如下:
1. 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
思想:动态规划
表示以第(i, j)个元素作为正方形右下角时最大正方形的边长。
状态转移方程:
当,
当,
class Solution:
def maximalSquare(self, matrix: list) -> int:
if not matrix:
return 0
M, N = len(matrix), len(matrix[0])
dp = [[0 for i in range(N)] for j in range(M)]
for i in range(M):
dp[i][0] = 1 if matrix[i][0] == 1 else 0 # 初始化第1列
for j in range(N):
dp[0][j] = 1 if matrix[0][j] == 1 else 0 # 初始化第1行
for i in range(1, M):
for j in range(1, N):
if matrix[i][j] == 1:
dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
maxValue = 0
for i in range(M):
maxValue = max(max(dp[i]), maxValue)
print(maxValue)
return maxValue ** 2
s = Solution()
x = s.maximalSquare([[1,0,1,0,0],[1,0,1,1,1],[1,1,1,1,1],[1,0,0,1,0]])
print(x)
# 输出: 4
2. 在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大矩形,并返回其面积。
2.1. 思想:直方图。
先行题:计算直方图的最大面积 输入:heights = [2, 1, 5, 6, 2, 3],输出:10
分析:
(1)计算以第i个值作为矩形高的时候,最大的底边长是多少。需要记录left和right的索引,然后底边长为,对应面积为 。
(2)初始化左右边界,
(3)使用单调栈。当栈顶元素比第i个元素值大的时候,即,该栈顶元素的右边界确定为,然后将栈顶元素弹出。
(4)重复(3),直到栈顶元素时,更新左边界;如果栈空则使用初始值就好,最后将索引i入栈。
这样就可以找到比自己矮的矩形索引作为边界条件!!!
如图,以5为高的矩形边界 left = 1, right =4, width = 4-1-1=2, area = 2*5 = 10
# 空间复杂度O(n)
# 时间复杂度O(n)
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
if not heights:
return 0
left = [-1] * len(heights)
right = [len(heights)] * len(heights)
stack = []
for i in range(len(heights)): # 往左遍历
while stack and heights[stack[-1]] >= heights[i]:
k = stack.pop(-1)
right[k] = i
left[i] = stack[-1] if stack else -1
stack.append(i)
area = max([heights[i]*(right[i]-left[i]-1) for i in range(len(heights))])
return area
计算出直方图面积之后,就可以对01数据进行分析了。
状态转移方程为
但是其实dp[i]存储的时某一行的直方图heights!!!只需要申请1*n的空间就可以了,不需要m*n。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
if not heights:
return 0
left = [-1] * len(heights)
right = [len(heights)] * len(heights)
stack = []
for i in range(len(heights)):
while stack and heights[stack[-1]] >= heights[i]:
k = stack.pop(-1)
right[k] = i
left[i] = stack[-1] if stack else -1
stack.append(i)
area = max([heights[i]*(right[i]-left[i]-1) for i in range(len(heights))])
return area
def maximalRectangle(self, matrix: List[List[str]]) -> int:
if not matrix:
return 0
# dp[i][j]用来存储当前以(i,j)为底的柱状高度,但是为了节省空间,使用dp[j]代替二维数组
dp = [0]*len(matrix[0])
for j in range(len(matrix[0])):
dp[j] = 1 if matrix[0][j] == '1' else 0
maxArea = self.largestRectangleArea(dp)
for i in range(1, len(matrix)):
for j in range(len(matrix[0])):
dp[j] = dp[j] + 1 if matrix[i][j] == '1' else 0
maxArea = max(maxArea, self.largestRectangleArea(dp))
return maxArea
2.2. 思想:动态规划。和求最大正方形不同,dp列表需要存储两个值 高和宽。
——(下面的是错的,目前还没想到好方法,但是hr告诉我可以这样操作,我也很头疼!!!)
分析:
(1)求dp第一行时,存在矩阵时高度只能为1;求dp第一列时,存在矩阵时宽度只能为1。不存在时高和宽都为0。
(2)从下标开始动态规划。
如果,则
如果,则要分情况讨论:
A. 如果,那么只可能从列矩形(宽为1)或行矩形(高为1)中选取面积最大的一种。
对于元素,由于,因此只能选取红色框的列矩形或者蓝色框的行矩形,很明显,红色矩形面积更大,因此,即
如果行矩形面积更大,则
B. 如果,那么
显然,元素(2,4)作为矩形右下角,可以到最大面积就是2*3=6
class Solution:
def maximalSquare(self, matrix: list) -> int:
if not matrix:
return 0
M, N = len(matrix), len(matrix[0])
dp = [[[0, 0] for i in range(N)] for j in range(M)]
# 求dp[0][0]元素
if matrix[0][0] == 1:
dp[0][0] = [1,1]
# 求dp第一行
for i in range(1, M):
if matrix[i][0] == 1:
dp[i][0][0] = dp[i - 1][0][0] + 1
dp[i][0][1] = 1
# 求dp第一列
for j in range(1, N):
if matrix[0][j] == 1:
dp[0][j][0] = 1
dp[0][j][1] = dp[0][j-1][0] + 1
maxValue = 0 # 记录最大面积
for i in range(1, M):
for j in range(1, N):
if matrix[i][j] == 1: # 如果第(i,j)个元素为1,才更新dp矩阵
if matrix[i-1][j-1] == 0: # 检查(i-1, j-1)元素是否为0,如果为0的话,则只选取最大的那个矩阵高和宽存储
if dp[i - 1][j][0] >= dp[i][j-1][1]: # 如果列矩阵面积更大,更新高
dp[i][j][0] = dp[i - 1][j][0] + 1
dp[i][j][1] = 1
else: # 如果行矩阵面积更大,更新宽
dp[i][j][0] = 1
dp[i][j][1] = dp[i][j-1][1] + 1
else:# 如果(i-1, j-1)元素为1,则高和宽都更新
dp[i][j][0] = dp[i - 1][j][0] + 1
dp[i][j][1] = dp[i][j - 1][1] + 1
maxValue = max(dp[i][j][0] * dp[i][j][1], maxValue)
return maxValue # 返回最大面积
s = Solution()
x = s.maximalSquare([[1, 0, 1, 0, 0], [1, 0, 1, 1, 1], [1, 1, 1, 1, 1], [1, 0, 0, 1, 0]])
print(x)
# 输出6 (因为2*3=6)