leetcode
leetcode 51 ~ 100
矩阵置零

矩阵置零

难度:

标签:

题目描述

给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法

 

示例 1:

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]

示例 2:

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]

 

提示:

  • m == matrix.length
  • n == matrix[0].length
  • 1 <= m, n <= 200
  • -231 <= matrix[i][j] <= 231 - 1

 

进阶:

  • 一个直观的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
  • 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
  • 你能想出一个仅使用常量空间的解决方案吗?

代码结果

运行时间: 52 ms, 内存: 15.1 MB


/*
 * Problem Statement:
 * Given an m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.
 * 
 * Approach:
 * 1. Identify rows and columns that need to be zeroed using an extra marker array.
 * 2. Use Java Streams to simplify the code and make it more readable.
 */
 
import java.util.stream.IntStream;
 
public class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        boolean[] zeroRows = new boolean[m];
        boolean[] zeroCols = new boolean[n];
 
        // Find rows and columns that need to be zeroed
        IntStream.range(0, m).forEach(i -> {
            IntStream.range(0, n).forEach(j -> {
                if (matrix[i][j] == 0) {
                    zeroRows[i] = true;
                    zeroCols[j] = true;
                }
            });
        });
 
        // Zero out rows
        IntStream.range(0, m).forEach(i -> {
            if (zeroRows[i]) {
                IntStream.range(0, n).forEach(j -> matrix[i][j] = 0);
            }
        });
 
        // Zero out columns
        IntStream.range(0, n).forEach(j -> {
            if (zeroCols[j]) {
                IntStream.range(0, m).forEach(i -> matrix[i][j] = 0);
            }
        });
    }
}

解释

方法:

该题解的思路是使用第一行和第一列来标记对应的行和列是否需要置零。首先,用两个变量 row0_flag 和 col0_flag 分别记录第一行和第一列是否包含 0。然后,遍历除第一行和第一列之外的所有元素,如果某个元素为 0,就将它所在行的第一个元素和所在列的第一个元素设为 0。接下来,再次遍历除第一行和第一列之外的所有元素,如果它所在行或列的第一个元素为 0,就将该元素设为 0。最后,根据 row0_flag 和 col0_flag 的值决定是否需要将第一行和第一列置零。

时间复杂度:

O(mn)

空间复杂度:

O(1)

代码细节讲解

🦆
在检查第一行和第一列是否包含0时,为什么不直接在最初的遍历中标记需要置零的行和列,而是单独设置了两个标记变量 row0_flag 和 col0_flag?
使用 row0_flag 和 col0_flag 的主要原因是第一行和第一列也用作标记数组的一部分。如果在最初的遍历中直接用第一行和第一列来标记它们自己是否包含0,则后续在标记其他行和列时可能会错误地改变第一行和第一列的标记值。因此,使用单独的标记变量可以保证在处理矩阵的其他部分时,不会影响到对第一行和第一列是否包含0的正确记录。
🦆
如果第一行或第一列本身包含0,那么在后续的遍历中,如何确保不会错误地将本来不需要置零的行或列置零?
这正是为什么要使用 row0_flag 和 col0_flag 的原因。这两个标记变量单独记录第一行和第一列是否包含0,独立于矩阵的其他部分。这样,在处理完矩阵的其他部分后,只有在这两个标记变量指示第一行或第一列包含0的情况下,才会将它们置零。这避免了因为标记其他行列时对第一行或第一列错误置零的风险。
🦆
该算法在遍历矩阵时,是否考虑了矩阵只有一行或一列的特殊情况?如果存在这种情况,该算法是否需要做出调整?
该算法确实考虑了矩阵只有一行或一列的情况。即使矩阵只有一行或一列,使用 row0_flag 和 col0_flag 依然可以正确处理。这是因为它们将单独记录是否有元素为0,而不依赖于矩阵的其他部分。因此,该算法在处理只有一行或一列的矩阵时不需要特别调整。
🦆
在将第一行和第一列的元素设为标记值(0)时,这种操作是否有可能影响到算法判断其他行列是否需要置零的逻辑?
在标记第一行和第一列的元素设为0时,这种操作实际上是基于矩阵中其他元素的状态。因此,只有当确定某行或某列中至少有一个元素为0时,才会将对应的第一行或第一列的元素设置为0。这种方法确保了只有那些真正需要置零的行或列被标记。此外,由于使用了 row0_flag 和 col0_flag 进行特殊的首行首列检查,所以不会影响到这两行的处理逻辑。

相关问题

生命游戏

根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

  1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
  2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
  3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
  4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

 

示例 1:

输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]

示例 2:

输入:board = [[1,1],[1,0]]
输出:[[1,1],[1,1]]

 

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 25
  • board[i][j]01

 

进阶:

  • 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
  • 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?