[LeetCode] 694. Number of Distinct Islands 不同島嶼的個數


 

Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.

Count the number of distinct islands. An island is considered to be the same as another if and only if one island can be translated (and not rotated or reflected) to equal the other.

Example 1:

11000
11000
00011
00011

Given the above grid map, return 1.

 

Example 2:

11011
10000
00001
11011

Given the above grid map, return 3.

Notice that:

11
1

and

 1
11

are considered different island shapes, because we do not consider reflection / rotation.

 

Note: The length of each dimension in the given grid does not exceed 50.

 

這道題讓我們求不同島嶼的個數,是之前那道 Number of Islands 的拓展,難點是如何去判斷兩個島嶼是否是不同的島嶼,首先1的個數肯定是要相同,但是1的個數相同不能保證一定是相同的島嶼,比如例子2中的那兩個島嶼的就不相同,就是說兩個相同的島嶼通過平移可以完全重合,但是不能旋轉。如何來判斷呢,可以發現可以通過相對位置坐標來判斷,比如使用島嶼的最左上角的1當作基點,那么基點左邊的點就是 (0,-1),右邊的點就是 (0,1), 上邊的點就是 (-1,0),下面的點就是 (1,0)。則例子1中的兩個島嶼都可以表示為 [(0,0), (0,1), (1,0), (1,1)],點的順序是基點-右邊點-下邊點-右下點。通過這樣就可以判斷兩個島嶼是否相同了,下面這種解法沒有用數組來存,而是 encode 成了字符串,比如這四個點的數組就存為 "0_0_0_1_1_0_1_1_",然后把字符串存入集合 unordered_set 中,利用其自動去重復的特性,就可以得到不同的島嶼的數量啦,參見代碼如下:

 

解法一:

class Solution {
public:
    vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
    int numDistinctIslands(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        unordered_set<string> res;
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1 && !visited[i][j]) {
                    set<string> s;
                    helper(grid, i, j, i, j, visited, s);
                    string t = "";
                    for (auto str : s) t += str + "_";
                    res.insert(t);
                }
            }
        }
        return res.size();
    }
    void helper(vector<vector<int>>& grid, int x0, int y0, int i, int j, vector<vector<bool>>& visited, set<string>& s) {
        int m = grid.size(), n = grid[0].size();
        visited[i][j] = true;
        for (auto dir : dirs) {
            int x = i + dir[0], y = j + dir[1];
            if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == 0 || visited[x][y]) continue;
            string str = to_string(x - x0) + "_" + to_string(y - y0);
            s.insert(str);
            helper(grid, x0, y0, x, y, visited, s);
        }
    }
};

 

當然我們也可以不 encode 字符串,直接將相對坐標存入數組中,然后把整個數組放到集合 set 中,還是會去掉相同的數組,而且這種解法直接在 grid 數組上標記訪問過的位置,寫起來更加簡潔了,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
    int numDistinctIslands(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        set<vector<pair<int, int>>> res;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] != 1) continue;
                vector<pair<int, int>> v;
                helper(grid, i, j, i, j, v);
                res.insert(v);
            }
        }
        return res.size();
    }
    void helper(vector<vector<int>>& grid, int x0, int y0, int i, int j, vector<pair<int, int>>& v) {
        int m = grid.size(), n = grid[0].size();
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] <= 0) return;
        grid[i][j] *= -1;
        v.push_back({i - x0, j - y0});
        for (auto dir : dirs) {
            helper(grid, x0, y0, i + dir[0], j + dir[1], v);
        }
    }
};

 

既然遞歸 DFS 可以,那么迭代的 BFS 就坐不住了,其實思路沒什么區別,這種類似迷宮遍歷的題都是一個套路,整體框架都很像,細枝末節需要改改就行了,參見代碼如下:

 

解法三:

class Solution {
public:
    vector<vector<int>> dirs{{0,-1},{-1,0},{0,1},{1,0}};
    int numDistinctIslands(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        set<vector<pair<int, int>>> res;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] != 1) continue;
                vector<pair<int, int>> v;
                queue<pair<int, int>> q{{{i, j}}};
                grid[i][j] *= -1;
                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 || grid[x][y] <= 0) continue;
                        q.push({x, y});
                        grid[x][y] *= -1;
                        v.push_back({x - i, y - j});
                    }
                }
                res.insert(v);
            }
        }
        return res.size();
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/694

 

類似題目:

Number of Islands

Number of Distinct Islands II

 

參考資料:

https://leetcode.com/problems/number-of-distinct-islands/

https://leetcode.com/problems/number-of-distinct-islands/discuss/150037/DFS-with-Explanations

https://leetcode.com/problems/number-of-distinct-islands/discuss/108474/JavaC%2B%2B-Clean-Code

https://leetcode.com/problems/number-of-distinct-islands/discuss/108475/Java-very-Elegant-and-concise-DFS-Solution(Beats-100)

 

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


免責聲明!

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



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