[LeetCode] 305. Number of Islands II 島嶼的數量之二


 

A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand operation which turns the water at position (row, col) into a land. Given a list of positions to operate, count the number of islands after each addLand operation. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example:

Input: m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]
Output: [1,1,2,3]

Explanation:

Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 represents land).

0 0 0
0 0 0
0 0 0

Operation 1: addLand(0, 0) turns the water at grid[0][0] into a land.

1 0 0
0 0 0   Number of islands = 1
0 0 0

Operation 2: addLand(0, 1) turns the water at grid[0][1] into a land.

1 1 0
0 0 0   Number of islands = 1
0 0 0

Operation 3: addLand(1, 2) turns the water at grid[1][2] into a land.

1 1 0
0 0 1   Number of islands = 2
0 0 0

Operation 4: addLand(2, 1) turns the water at grid[2][1] into a land.

1 1 0
0 0 1   Number of islands = 3
0 1 0

Follow up:

Can you do it in time complexity O(k log mn), where k is the length of the positions?

 

這道題是之前那道 Number of Islands 的拓展,難度增加了不少,因為這次是一個點一個點的增加,每增加一個點,都要統一一下現在總共的島嶼個數,最開始初始化時沒有陸地,如下:

0 0 0
0 0 0
0 0 0

假如在(0, 0)的位置增加一個陸地,那么此時島嶼數量為1:

1 0 0
0 0 0
0 0 0

假如再在(0, 2)的位置增加一個陸地,那么此時島嶼數量為2:

1 0 1
0 0 0
0 0 0

假如再在(0, 1)的位置增加一個陸地,那么此時島嶼數量卻又變為1:

1 1 1
0 0 0
0 0 0

假如再在(1, 1)的位置增加一個陸地,那么此時島嶼數量仍為1:

1 1 1
0 1 0
0 0 0

為了解決這種陸地之間會合並的情況,最好能夠將每個陸地都標記出其屬於哪個島嶼,這樣就會方便統計島嶼個數。這種群組類問題,很適合使用聯合查找 Union Find 來做,又叫並查集 Disjoint Set,LeetCode 中使用這種解法的題目還不少呢,比如 Friend CirclesGraph Valid TreeRedundant Connection II 等等。一般來說,UF 算法的思路是每個個體先初始化為不同的群組,然后遍歷有關聯的兩個個體,如果發現其 getRoot 函數的返回值不同,則手動將二者加入一個群組,然后總群組數自減1。這里就要分別說一下 root 數組,和 getRoot 函數。兩個同群組的個體,通過 getRoot 函數一定會返回相同的值,但是其在 root 數組中的值不一定相同,可以類比成 getRoot 函數返回的是祖先,如果兩個人的祖先相同,那么其是屬於一個家族的(這里不是指人類共同的祖先哈)。root 可以用數組或者 HashMap 來表示,如果個體是數字的話,那么數組就 OK,如果個體是字符串的話,可能就需要用 HashMap 了。root 數組的初始化可以有兩種,可以均初始化為 -1,或者都初始化為不同的數字,博主一般喜歡初始化為不同的數字。getRoot 函數的寫法也可用遞歸或者迭代的方式,可參見博主之前的帖子 Redundant Connection II 中的討論部分。這么一說感覺 UF 算法的東西還蠻多的,啥時候博主寫個 UF 總結貼吧。

那么具體來看這道題吧,此題跟經典的 UF 使用場景有一點點的區別,因為一般的場景中兩個個體之間只有兩種關系,屬於一個群組或者不屬於同一個群組,而這道題里面由於 water 的存在,就多了一種情況,只需要事先檢測一下當前位置是不是島嶼就行了,總之問題不大。一般來說 root 數組都是使用一維數組,方便一些,那么這里就可以將二維數組 encode 為一維的,於是需要一個長度為 m*n 的一維數組來標記各個位置屬於哪個島嶼,假設每個位置都是一個單獨島嶼,島嶼編號可以用其坐標位置表示,但是初始化時將其都賦為 -1,這樣方便知道哪些位置尚未變成島嶼。然后開始遍歷陸地數組,若某個島嶼位置編碼的 root 值不為 -1,說明這是一個重復出現的位置,不需要重新計算了,直接將 cnt 加入結果 res 中。否則將其島嶼編號設置為其坐標位置,然后島嶼計數加1,此時開始遍歷其上下左右的位置,遇到越界或者島嶼標號為 -1 的情況直接跳過,現在知道初始化為 -1 的好處了吧,遇到是 water 的地方直接跳過。否則用 getRoot 來查找鄰居位置的島嶼編號,同時也用 getRoot 來查找當前點的編號,這一步就是經典的 UF 算法的操作了,因為當前這兩個 land 是相鄰的,它們是屬於一個島嶼,所以其 getRoot 函數的返回值 suppose 應該是相等的,但是如果返回值不同,說明需要合並島嶼,將兩個返回值建立關聯,並將島嶼計數 cnt 減1。當遍歷完當前點的所有鄰居時,該合並的都合並完了,將此時的島嶼計數 cnt 存入結果中,參見代碼如下:

 

class Solution {
public:
    vector<int> numIslands2(int m, int n, vector<vector<int>>& positions) {
        vector<int> res;
        int cnt = 0;
        vector<int> roots(m * n, -1);
        vector<vector<int>> dirs{{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
        for (auto &pos : positions) {
            int id = n * pos[0] + pos[1];
            if (roots[id] != -1) {
                res.push_back(cnt);
                continue;
            }
            roots[id] = id;
            ++cnt;
            for (auto dir : dirs) {
                int x = pos[0] + dir[0], y = pos[1] + dir[1], cur_id = n * x + y;
                if (x < 0 || x >= m || y < 0 || y >= n || roots[cur_id] == -1) continue;
                int p = findRoot(roots, cur_id), q = findRoot(roots, id);
                if (p != q) {
                    roots[p] = q;
                    --cnt;
                }
            }
            res.push_back(cnt);
        }
        return res;
    }
    int findRoot(vector<int>& roots, int id) {
        return (id == roots[id]) ? id : findRoot(roots, roots[id]);
    }
};

 

Github 同步地址:

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

 

類似題目:

Number of Islands

 

參考資料:

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

https://leetcode.com/problems/number-of-islands-ii/discuss/75470/Easiest-Java-Solution-with-Explanations

 

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


免責聲明!

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



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