1. 程式人生 > >[LeetCode] Number of Distinct Islands 不同島嶼的個數

[LeetCode] 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();
    }
};

類似題目:

參考資料: