面试题01.08. 零矩阵

题目:编写一种算法,若M * N矩阵中某个元素为0,则将其所在的行与列清零。

示例1:

输入:

[

    [1,1,1],

    [1,0,1],

    [1,1,1]

]

输出:

[

    [1,0,1],

    [0,0,0],

    [1,0,1]

]

示例2:

输入:

[

    [0,1,2,0],

    [3,4,5,2],

    [1,3,1,5]

]

输出:

[

    [0,0,0,0],

    [0,4,5,0],

    [0,3,1,0]

]

 1. 思考

这题有个需要注意的点,不要将清空的元素误当作零元素,这样会导致最后整个矩阵都被清空。比如下图。

其中黄色块表示非零元素,白色块表示零元素,灰色块表示由于零元素而置空的行列中的元素。

如果按照元素顺序依次遍历判断是否需要置空所在行列的话,遍历到白色块,将所在行列元素都置空,遍历到灰色块会误认为其本来就是零元素,再次置空所在行列,这样就会造成误置空的操作。

所以需要将查询记录零元素所在行列与置空操作分开来。

可以建立两个数组,分别记录行索引或者列索引是否存在零元素,然后根据这个行列记录,将对应的行或列元素都置零。这样就避免了置零后,影响后续判断的问题。

 

这样空间复杂度是O(n),因为需要额外数组记录行和列是否存在零元素。

有更巧妙的方法,利用矩阵的第一行和第一列记录后续行列的零元素信息,这样空间复杂度是O(1) 。

首行记录对应列是否存在零元素,首列记录对应行是否存在零元素,所以对于首行首列的清空操作应该在最后进行。具体操作如下:

1. 判断原首行,首列是否本身存在零元素,这样避免后续在首行记录列零元素位置,或首列记录行零元素位置后无法知晓是否需要存在零元素将首行/首列清零。

2. 遍历首行/首列之外的元素,如果是零元素,首行/首列对应位置元素清零。如下图,黄色/白色块为遍历查询零元素的区域,白色块是零元素,绿色块/灰色块是用于记录行列零元素位置的区域,灰色块是遍历区域零元素的行列位置。

3. 将第一行首元素外的零元素对应的列置零。这边注意之所以跳过首元素,是为了防止[0, 0]元素为零的情况下,如果此时判断首元素,发现是零,将第一列整个置零,就丢失了零元素行位置的信息。

 

4. 将第一列首元素外的零元素对应的行置零。

 

 

5. 最后根据步骤1中首行首列自身是否存在零元素的信息,按需将首行/首列清零。

 

 2. 代码实现

public void setZeroes(int[][] matrix) {
    // 记录行数
    int rowNum = matrix.length;
    // 记录列数
    int colNum = matrix[0].length;
    // 记录首行是否存在零元素
    boolean firstRowHasZero = false;
    for (int col = 0; col < colNum; col++) {
        if (matrix[0][col] == 0) {
            firstRowHasZero = true;
            break;
        }
    }
    // 记录首列是否存在零元素
    boolean firstColHasZero = false;
    for (int row = 0; row < rowNum; row++) {
        if (matrix[row][0] == 0) {
            firstColHasZero = true;
            break;
        }
    }
    // 查询首列/首行之外的元素
    // 如果存在零元素,将对应首行列位置置零
    for (int row = 1; row < rowNum; row++) {
        for (int col = 1; col < colNum; col++) {
            if (matrix[row][col] == 0) {
                matrix[row][0] = 0;
                matrix[0][col] = 0;
            }
        }
    }
    // 判断首行首元素以外元素
    // 存在零元素则将对应列置零
    for (int col = 1; col < colNum; col++) {
        if (matrix[0][col] == 0) {
            setColZero(matrix, col);
        }
    }
    // 判断首列首元素以外的元素
    // 存在零元素则将对应行置零
    for (int row = 1; row < rowNum; row++) {
        if (matrix[row][0] == 0) {
            setRowZero(matrix, row);
        }
    }
    // 如果首行自身存在零元素,置零
    if (firstRowHasZero) {
        setRowZero(matrix, 0);
    }
    // 如果首列自身存在零元素,置零
    if (firstColHasZero) {
        setColZero(matrix, 0);
    }
}
// 将row行元素置零
private void setRowZero(int[][] matrix, int row) {
    int colNum = matrix[0].length;
    for (int col = 0; col < colNum; col++) {
        matrix[row][col] = 0;
    }
}
// 将col列元素置零
private void setColZero(int[][] matrix, int col) {
    int rowNum = matrix.length;
    for (int row = 0; row < rowNum; row++) {
        matrix[row][col] = 0;
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值