[LeetCode] 01 Matrix 零一矩陣


 

Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

Example 1: 
Input:

0 0 0
0 1 0
0 0 0

Output:

0 0 0
0 1 0
0 0 0

 

Example 2: 
Input:

0 0 0
0 1 0
1 1 1

Output:

0 0 0
0 1 0
1 2 1

 

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.

 

這道題給了我們一個只有0和1的矩陣,讓我們求每一個1到離其最近的0的距離,其實也就是求一個距離場,而求距離場那么BFS將是不二之選。剛看到此題時,我以為這跟之前那道 Shortest Distance from All Buildings 是一樣的,從每一個0開始遍歷,不停的更新每一個1的距離,但是這樣寫下來TLE了。后來我又改變思路,從每一個1開始BFS,找到最近的0,結果還是TLE,氣死人。后來逛論壇發現思路是對的,就是寫法上可以進一步優化,我們可以首先遍歷一次矩陣,將值為0的點都存入queue,將值為1的點改為INT_MAX。之前像什么遍歷迷宮啊,起點只有一個,而這道題所有為0的點都是起點,這想法,叼!然后開始BFS遍歷,從queue中取出一個數字,遍歷其周圍四個點,如果越界或者周圍點的值小於等於當前值加1,則直接跳過。因為周圍點的距離更小的話,就沒有更新的必要,否則將周圍點的值更新為當前值加1,然后把周圍點的坐標加入queue,參見代碼如下:

 

解法一:

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
        queue<pair<int, int>> q;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) q.push({i, j});
                else matrix[i][j] = INT_MAX;
            }
        }
        while (!q.empty()) {
            auto t = q.front(); q.pop();
            for (auto dir : dirs) {
                int x = t.first + dir[0], y = t.second + dir[1];
                if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] <= matrix[t.first][t.second] + 1) continue;
                matrix[x][y] = matrix[t.first][t.second] + 1;
                q.push({x, y});
            }
        }
        return matrix;
    }
};

 

下面這種解法是參考的qswawrq大神的帖子,他想出了一種二次掃描的解法,從而不用使用BFS了。這種解法也相當的巧妙,我們首先建立一個和matrix大小相等的矩陣res,初始化為很大的值,這里我們用INT_MAX-1,為甚么要減1呢,后面再說。然后我們遍歷matrix矩陣,當遇到為0的位置,我們將結果res矩陣的對應位置也設為0,這make sense吧,就不多說了。然后就是這個解法的精髓了,如果不是0的地方,我們在第一次掃描的時候,比較其左邊和上邊的位置,取其中較小的值,再加上1,來更新結果res中的對應位置。這里就明白了為啥我們要初始化為INT_MAX-1了吧,因為這里要加1,如果初始化為INT_MAX就會整型溢出,不過放心,由於是取較小值,res[i][j]永遠不會取到INT_MAX,所以不會有再加1溢出的風險。第一次遍歷我們比較了左和上的方向,那么我們第二次遍歷就要比較右和下的方向,注意兩種情況下我們不需要比較,一種是當值為0時,還有一種是當值為1時,這兩種情況下值都不可能再變小了,所以沒有更新的必要,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<int>> res(m, vector<int>(n, INT_MAX - 1));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0) res[i][j] = 0;
                else {
                    if (i > 0) res[i][j] = min(res[i][j], res[i - 1][j] + 1);
                    if (j > 0) res[i][j] = min(res[i][j], res[i][j - 1] + 1);
                }
            }
        }
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                if (res[i][j] != 0 && res[i][j] != 1) {
                    if (i < m - 1) res[i][j] = min(res[i][j], res[i + 1][j] + 1);
                    if (j < n - 1) res[i][j] = min(res[i][j], res[i][j + 1] + 1);
                }
            }
        }
        return res;
    }
};

 

史蒂芬大神的帖子中,他提出了一種變型的方法,沒有再區分左上右下,而是每次都跟左邊相比,但是需要每次把矩陣旋轉90度。他用python寫的解法異常的簡潔,貌似python中可以一行代碼進行矩陣旋轉,但是貌似C++沒有這么叼,矩陣旋轉寫起來還是需要兩個for循環,寫出來估計也不短,這里就不寫了,有興趣的童鞋可以自己試試寫一下,可以貼到留言板上哈~

 

參考資料:

https://leetcode.com/problems/01-matrix/

https://leetcode.com/problems/01-matrix/discuss/101021/java-solution-bfs

https://leetcode.com/problems/01-matrix/discuss/101039/java-33ms-solution-with-two-sweeps-in-on

https://leetcode.com/problems/01-matrix/discuss/101023/18-line-c-dp-solution-on-easy-to-understand

https://leetcode.com/problems/01-matrix/discuss/101102/short-solution-each-path-needs-at-most-one-turn

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM