题目:
给定一个值只有0,1的整型矩阵,求其中全是1 的所有矩形区域中,最大的矩形区域中 1 的数量。
如:
1 0 1 1
1 1 1 1
1 1 1 0
中最大矩形区域有6个1
思路:
转化成求解从每一行开始的直方图中最大矩形区域。
如:
上面的矩阵按第一行看,直方图高度为:
1,0,1,1
从第二行看为:
2,1,2,2
从第三行看为:
3,2,3,0
最简单的解法,遍历高度数组,每个位置,找到往左,往右能扩展的最远位置,然后计算面积:
def max_area_of_histogram0(heights):
max_area = 0
for i in range(len(heights)):
h = heights[i]
l, r = i, i
while l >= 0 and heights[l] >= h:
l -= 1
while r < len(heights) and heights[r] >= h:
r += 1
max_area = max(max_area, h * (r - l - 1))
return max_area
但该方法效率较低,大约O(n^2)复杂度。
此类问题可以用单调栈求解,书中解法如下:
def max_area_of_histogram(heights):
max_area = 0
stack = []
for i in range(len(heights)):
while len(stack) > 0 and heights[i] <= heights[stack[-1]]:
top = stack.pop()
w = i if len(stack) == 0 else i - stack[-1] - 1
cur_area = heights[top] * w
max_area = max(max_area, cur_area)
stack.append(i)
n = len(heights)
while len(stack) > 0:
top = stack.pop()
w = n if len(stack) == 0 else n - stack[-1] - 1
cur_area = w * heights[top]
max_area = max(max_area, cur_area)
return max_area
保持栈单调递增,碰到比栈顶保存的位置矮的柱子时,一直出栈,直到比当前柱子高或栈为空,出栈过程,计算出栈的柱子能扩展的最大面积。此间出栈的柱子都不比当前柱子矮,所以向右都能扩展到当前位置,出栈的柱子向左则只能扩展到它所在的位置,因为栈单调递增,左边的一定比它矮。比当前柱子矮的留在栈里,因为还可以继续往右扩展。
在遍历的时候,增加一次遍历,消除后面while循环:
def max_area_of_histogram2(heights):
max_area = 0
stack = []
for i in range(len(heights) + 1):
h = heights[i] if i < len(heights) else -1
while len(stack) > 0 and heights[stack[-1]] >= h:
top = stack.pop()
l = -1 if len(stack) == 0 else stack[-1]
cur_area = (i - l - 1) * heights[top]
max_area = max(max_area, cur_area)
stack.append(i)
return max_area
当然,也可以在高度数组最后加入一个0来实现
def max_area_of_histogram3(heights):
max_area = 0
stack = []
heights.append(0)
for i in range(len(heights)):
h = heights[i]
while len(stack) > 0 and heights[stack[-1]] >= h:
top = stack.pop()
l = -1 if len(stack) == 0 else stack[-1]
cur_area = (i - l - 1) * heights[top]
max_area = max(max_area, cur_area)
stack.append(i)
heights.pop()
return max_area
求解最大子矩阵大小:
def max_sub_matrix_size(matrix):
if matrix is None or len(matrix) == 0 or len(matrix[0]) == 0:
return 0
r, c = len(matrix), len(matrix[0])
max_area = 0
heights = [ 0 for _ in range(c) ]
for i in range(r):
for j in range(c):
if matrix[i][j] == 1:
heights[j] = heights[j] + 1
else:
heights[j] = 0
max_area = max(max_area, max_area_of_histogram3(heights))
return max_area
测试:
def test_max_area_of_histogram(count, minnum, maxnum):
heights = []
for i in range(count):
a = random.randint(minnum, maxnum)
heights.append(a)
area1 = max_area_of_histogram(heights)
area2 = max_area_of_histogram2(heights)
area3 = max_area_of_histogram3(heights)
if area1 != area2:
raise Exception('Error 1', area1, area2)
if area2 != area3:
raise Exception('Error 2', area2, area3)
def test_max_sub_matrix_size(r, c, percentage):
matrix = []
for _ in range(r):
row = []
for _ in range(c):
a = random.randint(0, 100)
if a < percentage:
row.append(0)
else:
row.append(1)
matrix.append(row)
ans = max_sub_matrix_size(matrix)
for i in range(r):
print(matrix[i])
print(ans)
if __name__ == '__main__':
test_max_area_of_histogram(10, 1, 100)
test_max_area_of_histogram(100, 1, 100)
test_max_area_of_histogram(1000, 1, 100)
test_max_area_of_histogram(10000, 1, 100)
test_max_area_of_histogram(100000, 1, 100)
test_max_sub_matrix_size(10, 10, 60)
test_max_sub_matrix_size(10, 10, 30)
test_max_sub_matrix_size(10, 10, 10)
结果:
[0, 1, 0, 0, 1, 0, 1, 0, 1, 1]
[0, 1, 1, 0, 1, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 1, 0, 0, 0, 1, 1]
[1, 1, 0, 0, 0, 0, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 1, 1, 0, 1]
[0, 0, 1, 0, 1, 1, 1, 1, 0, 1]
[1, 0, 0, 0, 1, 0, 1, 0, 1, 0]
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
[0, 0, 1, 1, 1, 0, 0, 1, 0, 1]
[1, 1, 0, 0, 0, 1, 1, 0, 0, 0]
6
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 0, 0, 0, 1, 1, 1, 1, 0]
[0, 0, 0, 1, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 0, 1, 0]
[0, 1, 1, 0, 1, 1, 0, 1, 1, 0]
[1, 0, 1, 1, 1, 1, 0, 0, 1, 0]
[1, 0, 0, 1, 1, 1, 1, 1, 1, 0]
[1, 0, 0, 0, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 0, 1, 0, 0, 1]
[1, 1, 0, 1, 1, 1, 0, 1, 1, 1]
12
[1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 0, 1]
[1, 1, 1, 1, 0, 1, 1, 1, 0, 1]
[0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
45