LeetCode 第73题:矩阵置零

88 阅读5分钟

LeetCode 第73题:矩阵置零

题目描述

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

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

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

示例 2:

示例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
  • -2^31 <= matrix[i][j] <= 2^31 - 1

解题思路

使用标记数组

最直观的解法是使用两个标记数组,分别记录需要置零的行和列。

关键点:

  1. 使用两个标记数组 rowZerocolZero 记录哪些行和列需要置零
  2. 遍历矩阵,标记含有0的行和列
  3. 再次遍历矩阵,根据标记数组置零

原地标记法

为了实现 O(1) 的空间复杂度,我们可以使用矩阵的第一行和第一列作为标记数组。

关键点:

  1. 使用两个变量 firstRowZerofirstColZero 记录第一行和第一列是否需要置零
  2. 使用矩阵的第一行和第一列作为标记数组
  3. 从第二行第二列开始遍历矩阵,如果元素为0,则将对应的第一行和第一列的元素置为0
  4. 根据第一行和第一列的标记,将对应的行和列置零
  5. 最后根据 firstRowZerofirstColZero 处理第一行和第一列

图解思路

算法步骤分析表

以 matrix = [[1,1,1],[1,0,1],[1,1,1]] 为例:

步骤操作矩阵状态说明
初始输入[[1,1,1],[1,0,1],[1,1,1]]原始矩阵
检查第一行列firstRowZero=false, firstColZero=false第一行和第一列没有0
标记(1,1)=0matrix[0][1]=0, matrix[1][0]=0在第一行第一列标记
置零根据标记[[1,0,1],[0,0,0],[1,0,1]]根据第一行和第一列的标记置零

状态/情况分析表

情况输入输出说明
无零矩阵[[1,1],[1,1]][[1,1],[1,1]]矩阵不变
第一行有零[[0,1],[1,1]][[0,0],[0,1]]第一行和第一列置零
中间有零[[1,1],[1,0]][[1,0],[0,0]]对应行列置零
全零矩阵[[0,0],[0,0]][[0,0],[0,0]]矩阵不变

代码实现

C# 实现

public class Solution {
    public void SetZeroes(int[][] matrix) {
        int m = matrix.Length;
        int n = matrix[0].Length;
        
        // 记录第一行和第一列是否有0
        bool firstRowZero = false;
        bool firstColZero = false;
        
        // 检查第一行是否有0
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                firstRowZero = true;
                break;
            }
        }
        
        // 检查第一列是否有0
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                firstColZero = true;
                break;
            }
        }
        
        // 使用第一行和第一列作为标记
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }
        
        // 根据第一行和第一列的标记,将对应的行和列置零
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        
        // 处理第一行
        if (firstRowZero) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
        
        // 处理第一列
        if (firstColZero) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }
    }
}

Python 实现

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        m, n = len(matrix), len(matrix[0])
        
        # 记录第一行和第一列是否有0
        first_row_zero = False
        first_col_zero = False
        
        # 检查第一行是否有0
        for j in range(n):
            if matrix[0][j] == 0:
                first_row_zero = True
                break
        
        # 检查第一列是否有0
        for i in range(m):
            if matrix[i][0] == 0:
                first_col_zero = True
                break
        
        # 使用第一行和第一列作为标记
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0
                    matrix[0][j] = 0
        
        # 根据第一行和第一列的标记,将对应的行和列置零
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
        
        # 处理第一行
        if first_row_zero:
            for j in range(n):
                matrix[0][j] = 0
        
        # 处理第一列
        if first_col_zero:
            for i in range(m):
                matrix[i][0] = 0

C++ 实现

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        
        // 记录第一行和第一列是否有0
        bool firstRowZero = false;
        bool firstColZero = false;
        
        // 检查第一行是否有0
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                firstRowZero = true;
                break;
            }
        }
        
        // 检查第一列是否有0
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                firstColZero = true;
                break;
            }
        }
        
        // 使用第一行和第一列作为标记
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }
        
        // 根据第一行和第一列的标记,将对应的行和列置零
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        
        // 处理第一行
        if (firstRowZero) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
        
        // 处理第一列
        if (firstColZero) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }
    }
};

执行结果

  • C# 执行用时:160 ms
  • C# 内存消耗:44.2 MB
  • Python 执行用时:44 ms
  • Python 内存消耗:15.2 MB
  • C++ 执行用时:12 ms
  • C++ 内存消耗:13.2 MB

代码亮点

  1. 🎯 使用原地算法,空间复杂度为 O(1)
  2. 💡 巧妙利用矩阵的第一行和第一列作为标记数组
  3. 🔍 分步处理,逻辑清晰
  4. 🎨 处理边界情况(第一行和第一列)

常见错误分析

  1. 🚫 没有单独处理第一行和第一列
  2. 🚫 在标记过程中就开始置零,导致错误传播
  3. 🚫 忘记检查第一行和第一列是否有0
  4. 🚫 使用额外空间而不是原地算法

解法对比

解法时间复杂度空间复杂度优点缺点
使用标记数组O(m*n)O(m+n)直观易懂需要额外空间
原地标记法O(m*n)O(1)空间优化实现复杂
使用特殊值标记O(m*n)O(1)无需额外变量依赖于数据范围

相关题目