LeetCode热题100——73.矩阵置零

63 阅读3分钟

题目描述

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

 

示例:

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

 

提示:

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

 

进阶:

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

解法

直观解法——标记数组

/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var setZeroes = function(matrix) {
    const m = matrix.length, n = matrix[0].length;
    const row = new Array(m).fill(false);
    const col = new Array(n).fill(false);
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (matrix[i][j] === 0) {
                row[i] = col[j] = true;
            }
        }
    }
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (row[i] || col[j]) {
                matrix[i][j] = 0;
            }
        }
    }   
};

设数组有mn列 创建标记数组 rowcol分别长度为mn

  • 循环遍历数组,若当前元素matrix[i][j]为零,将其对应的row[i]、col[j]设为true

  • 再次循环遍历数组,检查当前元素对应的row[i]、col[j],若有一项/都为true值,将当前数组元素设为0

时间、空间复杂度

时间复杂度:O(mn)

m 是矩阵的行数,n 是矩阵的列数。算法至多只需要遍历该矩阵两次O(2mn),即O(mn)。

空间复杂度:O(m+n)

创建了两个数组,长度分别为m、n,分别记录每一行或每一列是否有零出现。

利用原矩阵作为标记数组

var setZeroes = function(matrix) {
    const m = matrix.length, n = matrix[0].length;
    let flagCol0 = false, flagRow0 = false;
    for (let i = 0; i < m; i++) {
        if (matrix[i][0] === 0) {
            flagCol0 = true;
        }
    }
    for (let j = 0; j < n; j++) {
        if (matrix[0][j] === 0) {
            flagRow0 = true;
        }
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            if (matrix[i][j] === 0) {
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            if (matrix[i][0] === 0 || matrix[0][j] === 0) {
                matrix[i][j] = 0;
            }
        }
    }
    if (flagCol0) {
        for (let i = 0; i < m; i++) {
            matrix[i][0] = 0;
        }
    }
    if (flagRow0) {
        for (let j = 0; j < n; j++) {
            matrix[0][j] = 0;
        }
    }
};

我们将原矩阵的第一行和第一列分别作为上一解法中的两个标记数组col、row

在遍历矩阵查找0前,我们需要额外创建两个变量flagRow0、flagCol0用来储存第一行和第一列是否有0

  • 遍历从第二行第二列开始的子矩阵,

    • 如果 matrix[i][j]\text{matrix}[i][j]00,则将该行的标记位 matrix[i][0]\text{matrix}[i][0]该列的标记位 matrix[0][j]\text{matrix}[0][j] 置零。
  • 再次遍历子矩阵

    • 如果行标记 matrix[i][0]\text{matrix}[i][0]列标记 matrix[0][j]\text{matrix}[0][j]00,则将 matrix[i][j]\text{matrix}[i][j] 置零。
  • 最后,根据一开始保存的 flagCol0flagRow0 变量,来决定是否将第一列和第一行置零,避免它们在遍历子矩阵 中被覆盖后,信息丢失的问题。

时间、空间复杂度

时间复杂度:O(M×N)O(M \times N)

代码进行了多次遍历,但总遍历次数是常数次,因此总时间复杂度与矩阵元素总数成正比。

空间复杂度:O(1)O(1)

仅使用了 m, n, i, j 和两个布尔变量 flagCol0, flagRow0 等常数额外的空间,实现了原地修改。

参考链接:leetcode.cn/problems/se…