1.题目
2.思路
方法一:队列存储。时间O(N * M * Max(N, M), 近似 O(N * N * N))。空间O(N * N)
第一反应是,先挨个遍历,找到0,然后把行和列都置换为0,然后仔细一想,这样不对。因为这样会导致,后面本来不是0,不会产生让行列都换成0,但是由于前面的0,让后面非0的数字变成了0,这样就会一直错误遍历下去。
改进思路:记录一下哪些初始位置为0.只改变这些位置的行和列肯定不会出错。比如可以采取用一个vis[][]记录。我这里采取的是用队列存储,代码如下:
class Solution {
public void setZeroes(int[][] matrix) {
int n = matrix.length;
if(n == 0) return ;
int m = matrix[0].length;
Deque<int[]>d = new ArrayDeque();
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < m; j++){
if(matrix[i][j] == 0)
d.add(new int[]{i, j});
}
}
while(!d.isEmpty()){
int[]cur = d.poll();
int x = cur[0], y = cur[1];
putZero(matrix, x, y);
}
return ;
}
public void putZero(int[][] matrix, int x, int y){
int n = matrix.length;
int m = matrix[0].length;
for(int i = 0 ; i < n ; i++)
matrix[i][y] = 0;
for(int j = 0 ; j < m; j++)
matrix[x][j] = 0;
return ;
}
}
方法二 :借用两个临时变量,时间复杂度O(N * M)空间复杂度O(1)
上面的方法通过时间为1ms,超过50%。说明还有更快的方法。
核心思想有两点
- 1.让第一行和第一列的元素记录所在行列是否有0。(这样会导致原来第一行和第一列是否有0不确定,所以需要第2点,用两个临时变量记录)
- 2.用一个rowFlag记录第一列不同行的初始元素是否有0,只要有一个0,那么就赋值为true;用一个colFlag记录第一行不同列的初始元素是否有0,只要有一个0,那么就赋值为true;
步骤:
- 遍历第一行或者第一列,赋值rowFlag 和 colFlag
- 遍历其他行和其他列,赋值给第一行和第一列
- 先更新除第一行第一列的所有元素
- 最后更新第一行和第一列额元素
class Solution {
public void setZeroes(int[][] matrix) {
int n = matrix.length;
if(n == 0) return ;
int m = matrix[0].length;
// rowFlag记录第一列不同行是否有0
// colFlag记录第一行不同列是否有0
boolean rowFlag = false, colFlag = false;
// 初始化
for(int i = 0 ; i < n ; i++){
if(matrix[i][0] == 0)
rowFlag = true;
}
for(int j = 0 ; j < m ; j++){
if(matrix[0][j] == 0)
colFlag = true;
}
// 让第一行 和 第一列记录所在行列是否有0
for(int i = 1 ; i < n ; i++){
for(int j = 1 ; j < m ; j++){
if(matrix[i][j] == 0){
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}
// 除开第一行和第一列,替换为0
for(int i = 1 ; i < n ; i++){
for(int j = 1 ; j < m ; j++){
if(matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
}
}
// 第一行和第一列替换
if(rowFlag == true){
for(int i = 0 ; i < n ; i++){
matrix[i][0] = 0;
}
}
if(colFlag == true){
for(int j = 0 ; j < m; j++){
matrix[0][j] = 0;
}
}
return ;
}
}
这时候时间为0ms, 超过100%。但是空间还可以继续优化。
方法三:借助一个临时变量,时间O( N * M), 空间O(1)
在方法二的基础上,继续优化的话,回到方法二的两个核心思想:
- 1.让第一行和第一列的元素记录所在行列是否有0。(这样会导致原来第一行和第一列是否有0不确定,所以需要第2点,用两个临时变量记录)
- 2.用一个rowFlag记录第一列不同行的初始元素是否有0,只要有一个0,那么就赋值为true;用一个colFlag记录第一行不同列的初始元素是否有0,只要有一个0,那么就赋值为true;
这里的改进其实就是第 1 点不变,然后第2 点用一个变量来记录第一行和第一列元素是否初始位置有0.
我这里选取的是一个整形变量rowAndColFlag,初始值赋值为0,第一行第一列初始位置是否为0,有以下四种情况
-
rowAndColFlag == 0 代表第一行第一列都不为0
-
rowAndColFlag == 1 代表第一行不为0, 第一列为0
-
rowAndColFlag == -1 代表第一行为0, 第一列 不为0
-
rowAndColFlag == 2 代表第一行为0, 第一列为0;
其余步骤和方法二类似即可。
class Solution {
public void setZeroes(int[][] matrix) {
int n = matrix.length;
if(n == 0) return ;
int m = matrix[0].length;
// rowAndColFlag == 0 代表第一行第一列都不为0
// rowAndColFlag == 1 代表第一行不为0, 第一列为0
// rowAndColFlag == -1 代表第一行为0, 第一列 不为0
// rowAndColFlag == 2 代表第一行为0, 第一列为0;
int rowAndColFlag = 0;
// 初始化第一列
for(int i = 0 ; i < n ; i++){
if(matrix[i][0] == 0)
rowAndColFlag = 1;
}
for(int j = 0 ; j < m ; j++){
if(rowAndColFlag == 1){
if(matrix[0][j] == 0){
rowAndColFlag = 2;
break;
}
}
else{
if(matrix[0][j] == 0){
rowAndColFlag = -1;
break;
}
}
}
// System.out.println(rowAndColFlag);
// 让第一行 和 第一列记录所在行列是否有0
for(int i = 1 ; i < n ; i++){
for(int j = 1 ; j < m ; j++){
if(matrix[i][j] == 0){
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}
// 除开第一行和第一列,替换为0
for(int i = 1 ; i < n ; i++){
for(int j = 1 ; j < m ; j++){
if(matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
}
}
// 第一行和第一列替换
if(rowAndColFlag == 1 || rowAndColFlag == 2){
for(int i = 0 ; i < n ; i++){
matrix[i][0] = 0;
}
}
if(rowAndColFlag == -1 || rowAndColFlag == 2){
for(int j = 0 ; j < m; j++){
matrix[0][j] = 0;
}
}
return ;
}
}