LeetCode | Set Matrix Zeroes(矩陣相應行列清零)
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place.
Follow up:Did you use extra space?
A straight forward solution using O(mn) space is probably a bad idea.
A simple improvement uses O(m + n) space, but still not the best solution.
Could you devise a constant space solution?
題目解析:
碰到一個無法下手的問題,可以從最複雜的開始,一點一點去優化。既然要求不能使用額外空間,那麼利用額外空間就很容易做了,現在思考怎麼去應該額外空間,然後推出如何不適用額外空間來解題。
方案一:
建立一個一模一樣的二維陣列O(mn),其中也填充相同的資料。當在原陣列中碰到0,就在備份陣列中的i行j列相應的清除為0即可!這是最簡單最直觀的方案。
方案二:
如何進行優化?就跟n皇后問題一樣,只需要用一行陣列中某一個元素記錄哪一行需要清零,用一列陣列中的某一個元素記錄哪一列需要清零。然後遍歷這兩個陣列,對原陣列清零即可。這樣空間複雜度降低到O(m+n)。
方案三:
既然題目要求不適用額外空間,我們就從方案二找突破口。看能不能用某一列和某一行充當方案二的輔助空間。
----->思路一
一開始沒有想到充當輔助空間,只是想當(i,j)為0的時候,將這一行其他非零元素變為0,為0的元素變成-1(要保證這個資料在整個陣列中不出現),列也同理。當掃描到下一行(i,j)的時候,就相應的判斷(i-1,j)的值,如果為0,就無所謂;如果為-1就將該值變成-1,表示這一列是要清除的。
當掃描完以後,將所有的-1變成0即可。當然掃描每一行的某個值的時候,要記錄這一行之前是否出現0元素:沒有就直接看“頭頂”元素。如果出現要結合頭頂元素來判斷。
整個過程比較複雜,不過能實現要達到的目標。
----->思路二
先找到第一個為0的位置(i,j),那麼i行j列肯定要清零。這一行和這一列來當標記陣列。當其他位置(k1,k2)為0時,就讓(i,k2)和(k1,j)為0。掃描完以後,在根據這兩個標記陣列來進行相應的清空。
程式碼如下:
class Solution {
public:
void setZeroes(vector<vector<int> > &matrix) {
int m = matrix.size();
if(m < 1) return ;
int n = matrix.front().size();
//found first zero
int row = -1 , col = -1;;
for(int i = 0 ; i < m ; i ++) {
for(int j = 0 ; j < n ; j++) {
if(matrix[i][j] == 0) {
row = i;
col = j;
break;
}
}
if(row != -1) break;
}
if(row == -1) return;
//col
// for(int i = 0 ; i < n ; i++) matrix[row][i] = 0;
//row
// for(int i = 0 ; i < m ; i++) matrix[i][col] = 0;
//row , col to record if has zero
for(int i = 0 ; i < m ; i++) {
for(int j = 0 ; j < n ; j++) {
if(matrix[i][j] == 0) {
matrix[row][j] = 0;
matrix[i][col] = 0;
}
}
}
//fill col
for(int i = 0 ; i < n ; i++) {
if(i != col && matrix[row][i] == 0) {
//fill
for(int j = 0 ; j < m ; j++) {
matrix[j][i] = 0;
}
}
}
//fill row
for(int i = 0 ; i < m ; i++) {
if(i != row && matrix[i][col] == 0) {
//fill
for(int j = 0 ; j < n ; j++) {
matrix[i][j] = 0;
}
}
}
//col
for(int i = 0 ; i < n ; i++) matrix[row][i] = 0;
//row
for(int i = 0 ; i < m ; i++) matrix[i][col] = 0;
}
};
---->思路三:
既然我們要對某一行和某一列清零(i行j列),則相應的(i,0)和(0,j)肯定也要清零的。那麼可以讓0行和0列充當標記陣列。
需要注意:
1:標記陣列原始資料中是否已經存在0元素,要先做標記,然後忽略這兩個陣列存在的價值,專門去標記,最後再對兩個陣列操作。
2:第一遍的時候,只掃描,對標記陣列進行清零,其他元素不操作。最後再統一清零。
程式碼如下:
class Solution {
public:
void setZeroes(vector<vector<int> > &matrix) {
const int ROW = matrix.size();
if (ROW == 0)
return;
const int COL = matrix[0].size();
if (COL == 0)
return;
// we store the 0 information in
// the 1st row and 1st col
bool R00 = false;
for(int i = 0; i < COL; ++i)
if (matrix[0][i] == 0)
{
R00 = true;
break;
}
bool C00 = false;
for(int i = 0; i < ROW; ++i)
if (matrix[i][0] == 0)
{
C00 = true;
break;
}
// now traverse the remaining parts of
// the matrix and store 0 into the
// first row
for(int i = 1; i < ROW; ++i)
for(int j = 1; j < COL; ++j)
if (matrix[i][j] == 0)
{
matrix[i][0] = 0;
matrix[0][j] = 0;
}
// use the first row/col information to
// fill zeros
for(int i = 1; i < ROW; ++i)
if (matrix[i][0] == 0)
for(int j = 1; j < COL; ++j)
matrix[i][j] = 0;
for(int i = 1; i < COL; ++i)
if (matrix[0][i] == 0)
for(int j = 1; j < ROW; ++j)
matrix[j][i] = 0;
// Finally check the 1st row/col
if (R00)
fill(begin(matrix[0]), end(matrix[0]), 0);
if (C00)
for(int i = 0; i < ROW; ++i)
matrix[i][0] = 0;
return;
}
};