题目描述(中等难度)
给定一个矩阵,然后找到所有含有 0 的地方,把该位置所在行所在列的元素全部变成 0。
解法一
暴力解法,用一个等大的空间把给定的矩阵存起来,然后遍历这个矩阵,遇到 0 就把原矩阵的当前行,当前列全部变作 0,然后继续遍历。
public class Set_Matrix_Zeroes {
public static int[][] setZeroes(int[][] matrix) {
int row=matrix.length;
int col=matrix[0].length;
int[][] matrix_copy=new int[row][col];
//复制矩阵
for(int i=0;i<row;i++) {
for(int j=0;j<col;j++) {
matrix_copy[i][j]=matrix[i][j];
}
}
for(int i=0;i<row;i++) {
for(int j=0;j<col;j++) {
//找到0的位置
if(matrix_copy[i][j]==0) {
//将当前行,当前列位置置为0
setRowzeros(matrix,i);
setColzeroes(matrix,j);
}
}
}
return matrix;
}
private static void setRowzeros(int[][] matrix, int i) {
for(int j=0;j<matrix[0].length;j++) {
matrix[i][j]=0;
}
}
private static void setColzeroes(int[][] matrix, int j) {
for(int i=0;i<matrix.length;i++) {
matrix[i][j]=0;
}
}
public static void main(String args[]) {
int[][] ans= {{1,1,1},{1,0,1},{1,1,1}};
int[][] ss=setZeroes(ans);
for(int[] ssr:ss) {
for(int sst:ssr)
System.out.println(sst);
}
}
}
时间复杂度:O ( mn )。
空间复杂度:O(mn)。m 和 n 分别是矩阵的行数和列数。
解法二
空间复杂度可以优化一下,我们可以把哪一行有 0 ,哪一列有 0 都记录下来,然后最后统一把这些行,这些列置为 0。
public class Set_Matrix_Zeroes2 {
public static int[][] setZeroes(int[][] matrix) {
int row=matrix.length;
int col=matrix[0].length;
// 用两个bool 数组标记当前行和当前列是否需要置为0
boolean[] row_zero=new boolean[row];
boolean[] col_zero=new boolean[col];
for(int i=0;i<row;i++) {
for(int j=0;j<col;j++) {
//找到0的位置
if(matrix[i][j]==0) {
row_zero[i]=true;
col_zero[j]=true;
}
}
}
//将行标记为true的 行全部置为0
for(int i=0;i<row;i++) {
if(row_zero[i]) {
setrowzeros(matrix,i);
}
}
//将列标记为 false 的列全部置为 0
for (int i = 0; i < col; i++) {
if (col_zero[i]) {
setColZeroes(matrix, i);
}
}
return matrix;
}
private static void setColZeroes(int[][] matrix, int col) {
for (int i = 0; i < matrix.length; i++) {
matrix[i][col] = 0;
}
}
private static void setrowzeros(int[][] matrix, int row) {
for (int i = 0; i < matrix[row].length; i++) {
matrix[row][i] = 0;
}
}
public static void main(String args[]) {
int[][] ans= {{1,1,1},{1,0,1},{1,1,1}};
int[][] ss=setZeroes(ans);
for(int[] ssr:ss) {
for(int sst:ssr)
System.out.println(sst);
}
}
}
时间复杂度:O ( mn )。
空间复杂度:O(m + n)。m 和 n 分别是矩阵的行数和列数。
顺便说一下 leetcode 解法一说的解法,思想是一样的,只不过它没有用 bool 数组去标记,而是用两个 set 去存行和列。
package Set_Matrix_Zeroes;
import java.util.HashSet;
import java.util.Set;
public class Set_Matrix_Zeroes3 {
public void setZeroes(int[][] matrix) {
int R=matrix.length;
int C=matrix[0].length;
Set<Integer>row=new HashSet<Integer>();
Set<Integer>col=new HashSet<Integer>();
for(int i=0;i<R;i++) {
for(int j=0;j<C;j++) {
if(matrix[i][j]==0) {
row.add(i);
col.add(j);
}
}
}
for(int i=0;i<R;i++) {
for(int j=0;j<C;j++) {
if(row.contains(i)|| col.contains(j)) {
matrix[i][j]=0;
}
}
}
}
}
这里,有一个比自己巧妙的地方时,自己比较直接的用两个函数去将行和列分别置零,但很明显自己的算法会使得一些元素重复置零。而上边提供的算法,每个元素只遍历一次就够了,很棒。
解法三
继续优化空间复杂度,就是用给定的数组去存我们需要的数据,只要保证原来的数据不丢失就可以。
假设我们对问题足够的了解,假设存在一个数,矩阵中永远不会存在,然后我们就可以把需要变成 0 的位置先变成这个数,也就是先标记一下,最后再统一把这个数变成 0。直接贴下leetcode解法二的代码。
public class Set_Matrix_Zeroes4 {
public void setZeroes(int[][] matrix) {
int MODIFIED=-10000;
int row=matrix.length;
int col=matrix[0].length;
for(int i=0;i<row;i++) {
for(int j=0;j<col;j++) {
if(matrix[i][j]==0) {
for(int k=0;k<col;k++) {
if(matrix[i][k]!=0) {
matrix[i][k]=MODIFIED;
}
}
for(int k=0;k<row;k++) {
if(matrix[k][j]!=0) {
matrix[k][j]=MODIFIED;
}
}
}
}
}
for(int i=0;i<row;i++) {
for(int j=0;j<col;j++) {
if(matrix[i][j]==MODIFIED) {
matrix[i][j]=0;
}
}
}
}
}
时间复杂度:O ( mn )。
空间复杂度:O(1)。m 和 n 分别是矩阵的行数和列数。
当然,这个解法局限性很强,很依赖于样例的取值,我们继续想其他的方法。
回想一下解法二,我们用了两个 bool 数组去标记当前哪些行和那些列需要置零,我们能不能在矩阵中找点儿空间去存我们的标记呢?
可以的!因为当我们找到第一个 0 的时候,这个 0 所在行和所在列就要全部更新成 0,所以它之前的数据是什么就不重要了,所以我们可以把这一行和这一列当做标记位,0 当做 false,1 当做 true,最后像解法二一样,统一更新就够了。
如上图,找到第一个 0 出现的位置,把橙色当做解法二的列标志位,黄色当做解法二的行标志位。
如上图,我们首先需要初始化为 0,并且遇到之前是 0 的位置我们需要把它置为 1,代表当前行(或者列)最终要值为 0。
如上图,继续遍历找 0 的位置,找到后将对应的位置置为 1 即可。橙色部分的数字为 1 代表当前列要置为 0,黄色部分的数字为 1 代表当前行要置为 0。
它标记位直接用第一行和第一列,由于第一行和第一列不一定会被置为 0,所以需要用 isCol 变量来标记第一列是否需要置为 0,用 matrix[0][0] 标记第一行是否需要置为 0。它是将用 0 表示当前行(列)需要置 0,这一点也很巧妙。
public class Set_Matrix_Zeroes5 {
public void setZeroes(int[][] matrix) {
Boolean isCol = false;
int R = matrix.length;
int C = matrix[0].length;
for (int i = 0; i < R; i++) {
//判断第 1 列是否需要置为 0
if (matrix[i][0] == 0) {
isCol = true;
}
//找 0 的位置,将相应标记置 0
for (int j = 1; j < C; j++) {
if (matrix[i][j] == 0) {
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}
//根据标志,将元素置 0
for (int i = 1; i < R; i++) {
for (int j = 1; j < C; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) {
matrix[i][j] = 0;
}
}
}
//判断第一行是否需要置 0
if (matrix[0][0] == 0) {
for (int j = 0; j < C; j++) {
matrix[0][j] = 0;
}
}
//判断第一列是否需要置 0
if (isCol) {
for (int i = 0; i < R; i++) {
matrix[i][0] = 0;
}
}
}
}
这道题如果对空间复杂度没有要求就很简单了,对于空间复杂度的优化,充分利用给定的空间的思想很经典了。