【探索-中級演算法】矩陣置零
阿新 • • 發佈:2018-11-03
參考連結:https://www.jianshu.com/p/d0017b1e38c4
原地演算法:一種使用小的,固定數量的額外之空間來轉換資料的演算法。 當演算法執行時,輸入的資料通常會被要輸出的部份覆蓋掉。
O(mn) 的額外空間
public void setZeroes1(int[][] matrix) {
if (matrix == null || matrix[0] == null || matrix[0].length < 1) return;
// 結合額外的輔助陣列來判斷
// 如果 matrix[i][j] == 0 && !map[i][j] 則表示 matrix[i][j] 原本就為 0
// 如果 matrix[i][j] == 0 && map[i][j] 則表示 matrix[i][j] 是後面置為 0 的
boolean[][] map = new boolean[matrix.length][matrix[0].length];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == 0 && !map[i][j]) {
for (int k = 0; k < matrix[0].length; k++) {//先把那一橫置 0
if (matrix[i][k] == 0) continue;
matrix[i][k] = 0;
map[i][k] = true;
}
for (int k = 0; k < matrix.length; k++) {//先把那一列置 0
if (matrix[k][j] == 0) continue;//如果已經為 0 則跳過
matrix[k][j] = 0;
map[k][j] = true;
}
}
}
}
}
O(m+n) 的額外空間
// 不能邊修改邊遍歷,
// 因此先記錄符合要求的
// 某一行只要有一個,那麼該行全部置 0,列同理
public void setZeroes2(int[][] matrix) {
if (matrix == null || matrix[0] == null || matrix[0].length < 1) return;
boolean rw[] = new boolean[matrix.length];//記錄哪幾行需要被置 0
boolean cl[] = new boolean[matrix[0].length];//記錄哪幾列需要被置 0
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == 0) {
rw[i] = true;
cl[j] = true;
}
}
}
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (rw[i]||cl[j]) matrix[i][j] = 0;
}
}
}
關鍵點就是不能邊修改邊遍歷,因為修改後的值後影響對於原值的判斷,而且如果有某一行有一個為 0,則該行需要全部置為 0,對於列也同樣如此。
O(1) 的額外空間
public static void setZeroes3(int[][] matrix) {
if (matrix == null || matrix[0] == null || matrix[0].length < 1) return;
boolean row = false;
boolean col = false;
//判斷第一列是不是要置 0
for (int i = 0; i < matrix.length; i++) {
if (matrix[i][0]==0) {
col = true;
break;
}
}
//判斷第一行是不是要置 0,也需要從受元素遍歷起(即 j 不能從 1 開始),如{{0,1}} 的情況
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[0][j]==0) {
row = 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;// 該點所在行第一個
}
}
}
//要注意 c 的起始值為 1,如果從 0 開始則當 matrix[0][0] 為 0 時,
//會把第一列全部置為 0,從而影響後面的邏輯
for (int c = 1; c <matrix[0].length ; c++) {
if (matrix[0][c]==0) {//根據第一行的值來處理每一列
for (int r = 1; r < matrix.length; r++) {
matrix[r][c] = 0;
}
}
}
//r 的起始值同理
for (int r = 1; r < matrix.length; r++) {
if (matrix[r][0] == 0) {//根據第一列的值來處理每一行
for (int c = 1; c <matrix[0].length ; c++) {
matrix[r][c] = 0;
}
}
}
if (col) {
for (int i = 0; i < matrix.length; i++) {
matrix[i][0] = 0;
}
}
if (row) {
for (int i = 0; i < matrix[0].length; i++) {
matrix[0][i] = 0;
}
}
}
基於上一解法的思想,然後用第一行第一列輔助,節省空間。
我們可以使用原矩陣的第一行和第一列來記錄哪些行和列需要全部置為 0。
首先看第一行和第一列有沒有 0,如果有,記錄 flag 表示之後第一行或第一列需要全部置為 0;
遍歷除了第一行和第一列的所有元素,如果元素為 0,則將其所在行與第一列交點的元素,其所在列與第一行
交點的元素都置為 0。
根據第一行和第一列中的 0,將其他行列的元素置為 0;
根據記錄的 flag 將第一行和第一列置為 0。
演算法總結,當限定了空間時,就要考慮借用已有的空間進行輔助操作。