📌  相关文章
📜  查找由二进制矩阵中的所有 1 形成的最大“+”的大小(1)

📅  最后修改于: 2023-12-03 15:40:24.992000             🧑  作者: Mango

查找由二进制矩阵中的所有 1 形成的最大“+”的大小

介绍

在一个二进制矩阵中,找到由 1 组成的最大的“+”形状,返回该“+”形状的大小。例如:

111
111
111

由1构成的最大“+”形状的大小为5。对于该题目,我们需要遍历整个矩阵,找到“+”形状的中心点,然后再通过扩展来计算“+”形状的大小。在本篇文章中,我们将带领大家了解这道题的两种解决方法。

解法1

我们可以通过枚举矩阵中的每个点,并以该点为中心,向四个方向扩展,直到遇到0或边界为止,来计算以该中心点为中心的“+”形状的大小。具体过程如下:

class Solution:
    def max_plus(self, nums):
        m = len(nums)
        n = len(nums[0])
        ans = 0

        for i in range(m):
            for j in range(n):
                # 如果当前点为1,则计算以该点为中心的 “+” 大小
                if nums[i][j] == 1:
                    left, right, up, down = 0, 0, 0, 0
                    
                    # 向左扩展
                    k = j - 1
                    while k >= 0 and nums[i][k] == 1:
                        left += 1
                        k -= 1
                    
                    # 向右扩展
                    k = j + 1
                    while k < n and nums[i][k] == 1:
                        right += 1
                        k += 1
                    
                    # 向上扩展
                    k = i - 1
                    while k >= 0 and nums[k][j] == 1:
                        up += 1
                        k -= 1
                    
                    # 向下扩展
                    k = i + 1
                    while k < m and nums[k][j] == 1:
                        down += 1
                        k += 1
                    
                    # 计算当前中心点的“+”大小
                    ans = max(ans, min(left, right, up, down) * 4 + 1)
        
        return ans
复杂度分析
  • 时间复杂度:$O(mnmax(m, n))$,其中m和n分别为矩阵的行数和列数,因为对于每个中心点,都需要向四个方向扩展,最长可能扩展到整个矩阵的大小,即$m2 + n2$。
  • 空间复杂度:$O(1)$,只使用了几个常量来存储扩展的长度。
解法2

我们也可以通过预处理来优化解法1。具体的,我们可以找到每个位置上的左、右、上、下连续1的个数,并将这些信息保存在新的矩阵中。然后,我们可以通过枚举每个位置上的“+”形状的中心点,并利用预处理的信息来快速计算该“+”形状的大小。

代码实现
class Solution:
    def max_plus(self, nums):
        m = len(nums)
        n = len(nums[0])
        left_up = [[0 for _ in range(n)] for _ in range(m)]
        right_down = [[0 for _ in range(n)] for _ in range(m)]

        # 预处理左上角到右下角的连续1的个数
        for i in range(m):
            for j in range(n):
                if nums[i][j] == 0:
                    continue

                # 计算左侧连续1的个数
                if i == 0:
                    left_up[i][j] = 1
                else:
                    left_up[i][j] = left_up[i-1][j] + 1

                # 计算上侧连续1的个数
                if j == 0:
                    right_down[i][j] = 1
                else:
                    right_down[i][j] = right_down[i][j-1] + 1

        # 预处理右上角到左下角的连续1的个数
        for i in range(m-1, -1, -1):
            for j in range(n-1, -1, -1):
                if nums[i][j] == 0:
                    continue

                # 计算右侧连续1的个数
                if i == m-1:
                    right_down[i][j] = min(right_down[i][j], 1)
                else:
                    right_down[i][j] = min(right_down[i+1][j] + 1, right_down[i][j])

                # 计算下侧连续1的个数
                if j == n-1:
                    left_up[i][j] = min(left_up[i][j], 1)
                else:
                    left_up[i][j] = min(left_up[i][j+1] + 1, left_up[i][j])

        ans = 0

        # 枚举每个中心点,计算”+“大小
        for i in range(m):
            for j in range(n):
                if nums[i][j] == 0:
                    continue

                # 向上扩展
                k = left_up[i][j]
                
                # 向下扩展
                while k > 1:
                    if right_down[i + k - 1][j] >= k:
                        break
                    k -= 1

                ans = max(ans, (k-1)*4 + 1)

                # 向左扩展
                k = right_down[i][j]

                # 向右扩展
                while k > 1:
                    if left_up[i][j + k - 1] >= k:
                        break
                    k -= 1

                ans = max(ans, (k-1)*4 + 1)

        return ans
复杂度分析
  • 时间复杂度:$O(m*n)$,其中m和n分别为矩阵的行数和列数,因为只需要遍历整个矩阵一次,并进行两次预处理即可。
  • 空间复杂度:$O(m*n)$,需要额外使用左上角到右下角、右上角到左下角的数组来保存预处理结果。