首页 简历|笔试面试

73.矩阵置零

  • 25年9月4日 发布
  • 63.37KB 共5页
73.矩阵置零73.矩阵置零73.矩阵置零73.矩阵置零73.矩阵置零

73. 矩阵置零

题意

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

请使用 原地 算法。

这里给大家解释一下什么是原地算法。假设你现在是一个大厨,对,戴的帽子非

常高的那种。但是老板对你有一些苛刻的要求,比如说菜板非常小,但又不给你

提供额外的餐盘作为辅助,要你在这个小菜板上切碎很多很多蔬菜,比如说肉

丝、葱姜蒜、蒜苔等等。

换句话说,原地算法是指在算法的执行过程中,不需要使用额外的内存空间,而

是直接在原始输入数据的存储空间中进行计算。有时候,可能需要牺牲掉一些时

间。

难度

中等

示例

示例 1:

1677292978543-0f6c7610-10f2-42ed-9739-2a89f7c44c07.jpeg

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

输出:[[1,0,1],[0,0,0],[1,0,1]]

示例 2:

1677292986487-9bd9da8c-3c57-4fcc-b584-e87d9f5d3c08.jpeg

输入: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]]

分析

本题并不算难,读完题目,我们应该很快就能够想到利用两个辅助数组,分别来记录该

行/列是否有 0 出现过。

然后再遍历所有的元素,按照辅助数组来决定当前位置是否要置换为 0。

依照上述的思想,我们可以写出如下的代码:

class Solution {

public void setZeroes(int[][] matrix) {

int n = matrix.length;

int m = matrix[0].length;

boolean[] zeroInRow = new boolean[n];

boolean[] zeroInLine = new boolean[m];

for(int i = 0;i < n;i++)

for(int j = 0;j < m;j++)

if(matrix[i][j] == 0){

zeroInRow[i] = true;

zeroInLine[j] = true;

}

for(int i = 0;i < n;i++)

for(int j = 0;j < m;j++)

if(zeroInRow[i] || zeroInLine[j]){

matrix[i][j] = 0;

}

}

}

这样子确实能够解题,而且效果也不错!

1677292996241-96a2f819-eb99-43bf-88c8-c277adce4557.png

但是,能不能再优化优化?

时间复杂度方面已经优化的余地了,因为必须要对每个元素进行甄别或者修改,所以时间

复杂度至少为$ O(nm) $,那我们就考虑下,能不能对空间复杂度进行优化呢?

当前采取的方法,需要两个辅助数组来帮助我们定位 0,这样子的话,辅助数据的空间就

会随着矩阵大小而增加,有没有办法能够在常数的空间范围内,将这道题目解决呢?

我们来观察一下,标记数组是一个 boolean 类型的数组,每一位都由 0 和 1 组成,很容

易使得我们联想到另外一个同样由这两个组成的东西—— 二进制 !!!!

是的,我们可以用两个二进制来描述这个 boolean 数组!

而描述一个二进制数,只需要一个整型即可,于是乎,我们就能写出这样子的代码:

class Solution {

public void setZeroes(int[][] matrix) {

long n = 0;

long m = 0;

for(int i = 0;i < matrix.length;i++)

for(int j = 0;j < matrix[0].length;j++)

if(matrix[i][j] == 0){

n |= 1L << (i);

m |= 1L << (j);

}

for(int i = 0;i < matrix.length;i++)

for(int j = 0;j < matrix[0].length;j++)

if((((n >> i) & 1) != 0) || (((m >> j) & 1) != 0)){

matrix[i][j] = 0;

}

}

}

此时,原先代码中用于标记的 boolean 数组被我们用 n,m 取代了。

但是!!!

这份代码并不能通过所有的测试点,为什么呢?

原因在于 整型的限制 !!!

我们知道,整型的指示范围在-2147483648~2147483647 之间,也就是$ -2{31}至 2{31}-1

$,所以当矩阵的大小超过 32 的时候,我们的范围就不够用了,那么长整型 long 呢,虽

然大了很多,可惜仍然不能满足需求,所以我们必须另寻它路。

其实我们还能利用——每行每列的第 0 个元素!

如果我们提前利用两个变量来记录第 0 行以及第 0 列中是否含有 0,这时候,第 0 行和第

0 列都能为我们所用,用来标记当前行/列中是否存在 0,也就是 把第**0**行和第**0**

列作为一开始的两个标记数组,然后再按照这个信息来判断每个位置需不需要修改为 0。

最后再按照提前准备的两个变量判断需不需要把第 0 行和第 0 列修改为 0。

class Solution {

public void setZeroes(int[][] matrix) {

boolean row0 = false;

boolean line0 = false;

for (int[] ints : matrix)

if (ints[0] == 0) {

row0 = true;

break;

}

for(int i = 0;i < matrix[0].length;i++)

if(matrix[0][i] == 0){

line0 = true;

break;

}

for(int i = 1;i < matrix.length;i++)

for(int j = 1;j < matrix[0].length;j++){

if(matrix[i][j] == 0) {

matrix[0][j] = 0;

matrix[i][0] = 0;

}

}

for(int i = 1;i < matrix.length;i++)

for(int j = 1;j < matrix[0].length;j++){

if(matrix[0][j] == 0 || matrix[i][0] == 0) {

matrix[i][j] = 0;

}

}

for(int i = 0;i < matrix.length;i++){

if(row0)

matrix[i][0] = 0;

}

for(int i = 0;i < matrix[0].length;i++){

if(line0)

matrix[0][i] = 0;

}

}

}

1677293009114-ec836be1-fd5e-464e-8684-949ebeaecaa3.png

这时候我们就真正做到了利用常数级别的空间复杂度,帅气顺利地解决本题!

总结

其实算法就是用空间换取时间,用时间换取空间,或者是不断优化,利用更少的空间、更

少的时间来实现我们的目的。

力扣链接:https://leetcode.cn/problems/set-matrix-zeroes/

开通会员 本次下载免费

所有资料全部免费下载! 推荐用户付费下载获取返佣积分! 积分可以兑换商品!
一键复制 下载文档 联系客服