力扣73——矩陣置零

準備開一個力扣解題的系列,督促本身天天刷題,就從今天開始。
java

原題

給定一個 m x n 的矩陣,若是一個元素爲 0,則將其所在行和列的全部元素都設爲 0。請使用原地算法。git

示例 1:github

輸入: 
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
輸出: 
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]

示例 2:算法

輸入: 
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
輸出: 
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]

進階:優化

  • 一個直接的解決方案是使用 O(mn) 的額外空間,但這並非一個好的解決方案。
  • 一個簡單的改進方案是使用 O(m + n) 的額外空間,但這仍然不是最好的解決方案。
  • 你能想出一個常數空間的解決方案嗎?

原題url:https://leetcode-cn.com/problems/set-matrix-zeroes/url

解法

其實題目自己不難,只要判斷出哪些數字是0,將其所在行和列記錄一下, 最終所有置0便可,關鍵在於你所須要消耗的空間是多少。code

用一個數字

首先我想到的是用一個數字進行表示,用二進制表示,一共m + n位,其中前m位表示行,後n位表示列,矩陣中哪一個數字爲0,則其行列所在位的數字爲1,也就是加上相應的二進制數。爲了避免重複添加,能夠用&進行判斷。來看看代碼是什麼:對象

class Solution {
    public void setZeroes(int[][] matrix) {
        // 轉化爲二進制後,前m位表示列,後n位表示行
        int temp = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j] != 0) {
                    continue;
                }

                // 第j列是否已經被設置爲0
                int num = 1 << (matrix.length + j);
                if ((temp & num) != num) {
                    // 若是沒有,則加上
                    temp += num;
                }

                // 第i行是否已經被設置爲0
                num = 1 << i;
                if ((temp & num) != num) {
                    // 若是沒有,則加上
                    temp += num;
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                // 第j列是否已經被設置爲0
                int numCol = 1 << (matrix.length + j);
                // 第i行是否已經被設置爲0
                int numRow = 1 << i;
                if ((temp & numRow) == numRow || (temp & numCol) == numCol) {
                    // 若是有,則設置當前值爲0
                    matrix[i][j] = 0;
                }
            }
        }
    }
}

理論上沒什麼問題,提交以後報錯。當mn很大時,數字會很大,這個時候temp會越界。我想着是否是求2的冪用Math.pow(),而且 temp 的類型改成 long ,是否是就能夠了,說幹就幹:內存

class Solution {
    public void setZeroes(int[][] matrix) {
        // 轉化爲二進制後,前m位表示列,後n位表示行
        long temp = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j] != 0) {
                    continue;
                }

                // 第j列是否已經被設置爲0
                long num = (long)Math.pow(2, matrix.length + j);
                if ((temp & num) != num) {
                    // 若是沒有,則加上
                    temp += num;
                }

                // 第i行是否已經被設置爲0
                num = (long)Math.pow(2, i);
                if ((temp & num) != num) {
                    // 若是沒有,則加上
                    temp += num;
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                // 第j列是否已經被設置爲0
                long numCol = (long)Math.pow(2, matrix.length + j);
                // 第i行是否已經被設置爲0
                long numRow = (long)Math.pow(2, i);
                if ((temp & numRow) == numRow || (temp & numCol) == numCol) {
                    // 若是有,則設置當前值爲0
                    matrix[i][j] = 0;
                }
            }
        }
    }
}

好吧,依然不能夠,看來確實很大,最終仍是溢出變成負數了。看來得另尋他法了。leetcode

利用矩陣自己

若是1個數字不夠,那麼多來幾個數字應該也是不夠用的,並且若是用的太多也可能會增加到m + n,空間依舊比較多。這個時候我也想不出來,看了看別人的解法,讓我頓時領悟——利用矩陣自己。

就是利用矩陣的第一行和第一列來記錄須要置零的行和列,至於第一行和第一列是否須要置零,則能夠單獨拿兩個 boolean 對象來表示。(怎麼好的思路,爲啥我就是沒想到呢)來看看代碼:

class Solution {
    public void setZeroes(int[][] matrix) {
        // 用第一行和第一列表示當前行和當前列是否須要置0
        // 單獨計算第一行和第一列是否須要置0

        int row = matrix.length;
        int col = matrix[0].length;
        // 第一行是否須要置0
        boolean row0 = false;
        for (int i = 0; i < col; i++) {
            if (matrix[0][i] == 0) {
                row0 = true;
                break;
            }
        }
        // 第一列是否須要置0
        boolean col0 = false;
        for (int i = 0; i < row; i++) {
            if (matrix[i][0] == 0) {
                col0 = true;
                break;
            }
        }

        // 判斷每一行每一列是否須要置0
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                if (matrix[i][j] != 0) {
                    continue;
                }
                
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
        // 置0
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }

        // 第一行是否須要都置0
        if (row0) {
            for (int i = 0; i < col; i++) {
                matrix[0][i] = 0;
            }
        }
        // 第一列是否須要都置0
        if (col0) {
            for (int i = 0; i < row; i++) {
                matrix[i][0] = 0;
            }
        }
    }
}

終於經過了,執行用時:2ms,內存消耗:43.5MB。那麼是否能夠繼續優化呢?

利用矩陣自己 優化

首先,須要第一行和第一列都判斷一遍的嗎?能夠只判斷其中一個便可,好比只判斷第一列是否須要置零,那麼第一行是否須要置零就能夠依賴matrix[0][0]了。在置零的時候,也是將第一列單獨判斷便可。

須要注意的是,置零操做須要從後往前,由於matrix[0][0]會有雙重含義,因此最後判斷便可。來看看代碼:

class Solution {
    public void setZeroes(int[][] matrix) {
        // 第一列是否須要置零
        boolean col0 = false;
        int row = matrix.length;
        int col = matrix[0].length;

        // 判斷是否須要置零
        for (int i = 0; i < row; i++) {
            // 若是第一列不須要置零,而且第一列有數字是0,則col0設置爲true
            if (!col0 && matrix[i][0] == 0) {
                col0 = true;
            }

            for (int j = 1; j < col; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }

        // 置零,從後往前開始,由於若是從前日後,第一行若是由於第一列置爲0,會對以後結果誤導
        for (int i = row - 1; i >= 0; i--) {
            // 第一列不動
            for (int j = col - 1; j >= 1; j--) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
            // 第一列置零
            if (col0) {
                matrix[i][0] = 0;
            }
        }
    }
}

總結

以上就是這道題目個人解答過程了,不知道你們是否理解了。我準備把我刷力扣的過程記錄下來,做爲這個系列的內容,但願能和你們多多分享。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

相關文章
相關標籤/搜索