73. Set Matrix Zeroes

题目描述(中等难度)

在这里插入图片描述
给定一个矩阵,然后找到所有含有 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;
            }
        }
    }
}

这道题如果对空间复杂度没有要求就很简单了,对于空间复杂度的优化,充分利用给定的空间的思想很经典了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安替-AnTi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值