求最大子矩阵的大小

48 篇文章 0 订阅

题目:
给定一个值只有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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值