leetcode热题——矩阵置零

发布于:2025-07-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

矩阵置零

题目描述

题解

方法一:直观法:使用额外的布尔矩阵来标记

C++ 代码实现

复杂度分析

方法二:使用标记数组

C++ 代码实现

复杂度分析

方法三:使用两个标记变量

详细步骤

C++ 代码实现

复杂度分析

题目描述

给定一个 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) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个仅使用常量空间的解决方案吗?

题解

方法一:直观法:使用额外的布尔矩阵来标记

遍历原始矩阵 matrix,找到所有为 0 的元素;
在布尔矩阵 mark 中将它所在行列都标记为 true;
再次遍历原矩阵,如果它所在的行或列在 mark 中有 true,就将它置为 0。

C++ 代码实现
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();

        // 创建一个标记矩阵
        vector<vector<bool>> mark(m, vector<bool>(n, false));

        // Step 1: 遍历原矩阵,标记需要置零的行列
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    // 当前这一行、这一列都需要标记
                    for (int k = 0; k < n; k++) {
                        mark[i][k] = true;
                    }
                    for (int k = 0; k < m; k++) {
                        mark[k][j] = true;
                    }
                }
            }
        }

        // Step 2: 根据标记置 0
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (mark[i][j]) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
};
复杂度分析
  • 时间复杂度:O(m × n × (m + n)) ≈ O(m² + n²) 因为每遇到一个 0,就标记一整行一整列(有优化空间)
  • 空间复杂度:O(m × n) 使用了一个同样大小的布尔矩阵 mark

方法二:使用标记数组

我们可以创建一个大小为 m 的标记数组 row 和 n 个标记数组 col,用于标记哪些行和列需要置零。 把 matrix[i][j] 置零时,将对应的 row[i] 和 col[j] 标记为 true。 在遍历矩阵时,如果 row[i] 或 col[j] 为 true,则将 matrix[i][j] 置零。

C++ 代码实现
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<bool> row(m, false);
        vector<bool> col(n, false);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    row[i] = true;
                    col[j] = true;
                }
            }
            }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (row[i] || col[j]) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
    
};
复杂度分析
  • 时间复杂度:O(m * n),其中 m 和 n 分别为矩阵的行数和列数。
  • 空间复杂度:O(m + n),其中 m 和 n 分别为矩阵的行数和列数。

方法三:使用两个标记变量

我们要用 第一行 和 第一列 代替额外的空间来记录每一行和列是否应该设为 0。

但直接这样做有个问题:我们无法判断原来的第一行和第一列是否本身有 0,因为它们被重用了。
所以我们要 额外使用两个变量,分别记录第一行和第一列是否应该为 0:
row0Flag:第一行是否需要设置为 0
col0Flag:第一列是否需要设置为 0

详细步骤

Step 1:确定第一行和第一列是否需要置零(用两个变量标记)

bool row0Flag = false, col0Flag = false;
// 检查第一列
for (int i = 0; i < m; i++) {
    if (matrix[i][0] == 0) col0Flag = true;
}
// 检查第一行
for (int j = 0; j < n; j++) {
    if (matrix[0][j] == 0) row0Flag = true;
}

Step 2:从 (1,1) 开始遍历,使用第一行和第一列作为标记

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; // 标记这一列
        }
    }
}

Step 3:根据标记将剩余矩阵元素置零(除了第一行第一列)

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;
        }
    }
}

Step 4:根据两个标记变量处理第一行和第一列

if (col0Flag) {
    for (int i = 0; i < m; i++) matrix[i][0] = 0;
}
if (row0Flag) {
    for (int j = 0; j < n; j++) matrix[0][j] = 0;
}
C++ 代码实现
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();

        bool row0Flag = false, col0Flag = false;

        // Step 1: 检查第一列是否需要置零
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                col0Flag = true;
            }
        }

        // Step 1: 检查第一行是否需要置零
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                row0Flag = true;
            }
        }

        // Step 2: 用第一行和第一列做标记
        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;
                }
            }
        }

        // Step 3: 根据标记置零
        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;
                }
            }
        }

        // Step 4: 处理第一列
        if (col0Flag) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }

        // Step 4: 处理第一行
        if (row0Flag) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
    }
};
复杂度分析

时间复杂度:O(mn),其中 m 是矩阵的行数,n 是矩阵的列数。我们至多只需要遍历该矩阵两次。

空间复杂度:O(1),我们只需要常数空间存储若干变量。


网站公告

今日签到

点亮在社区的每一天
去签到