Google 2016 面試題5 | 島嶼計數2

題目描述

給出一個m行n列的網格地圖,每個位置爲0或1,0表示海水1表示陸地。一開始地圖全爲0(沒有陸地)。每次在一個位置加入一塊陸地,返回此時地圖中陸地的總塊數(相鄰陸地統計時爲同一塊陸地)。
Example:
操作#1: addLand(0, 0) turns the water at grid[0][0] into a land.
leetcode
操作#2: addLand(0, 1) turns the water at grid[0][1] into a land.
leetcode
操作#3: addLand(1, 2) turns the water at grid[1][2] into a land.
leetcode
操作#4: addLand(2, 1) turns the water at grid[2][1] into a land.
leetcode
返回答案數組: [1, 1, 2, 3]
你可以做到複雜度O(k log mn)嗎?其中k爲操作次數。

分析解答

對於一個靜態的地圖,統計島嶼個數可以使用dfs(類似於尋找一個圖中的連通塊個數),算法複雜度是O(m*n)。但是對於一個不斷更新的地圖,如果我們每次重新統計連通塊個數,複雜度爲O(m*n*k),其中k爲總操作個數。考慮到每次只有一個位置發生變化(從0變爲1),完全不必重新統一,該陸地的產生職能影響周圍四個位置。假設該陸地周圍有t(p至多爲4)個不連通的島嶼,那麼該陸地爲把這四個不同點島嶼合併爲一個島嶼,使得總島嶼數下降t-1個。因此我們需要維護島嶼之間的連通性,自然的我們想到了並查集。並查集是一種解決此類問題的強力數據結構,以此題爲例,初始時每個位置都是獨立的、互不連通的,每個位置都有一個標籤來identify自己,記錄在fa數組中,fa[i]爲i。當兩個位置p、q相鄰且都爲1時,這兩個位置需要統一它們的標籤(表示這兩個島嶼合併),即fa[p] = q。但是p、q的標籤可能已經被修改,因此我們需要通過getfather函數遞歸找到它們的真實標籤(getfather(i)的返回值也稱爲i的祖先),合併操作變爲fa[getfather(p)] = getfather(q)。爲了防止最壞情況下每次調用getfather函數都要經過m*n次遞歸,我們可以採用路徑壓縮的方法(詳見代碼中getfather函數),使得每個位置到其祖先的距離始終爲一個很小的常數(與m、n無關)。本題中總體時間複雜度爲O(m*n+k),其中每次並查集的查詢複雜度爲一個常數(不超過4)。

參考代碼

class Solution {
public:
    vector<int> numIslands2(int m, int n, vector<vector<int> >& positions) {
        bool land[m][n];
        int dr[4] = {1, 0, -1, 0};
        int dc[4] = {0, -1, 0, 1};
        int fa[m * n];
        int island = 0;
        vector<int> ret;

        // initialization
        memset(land, 0, sizeof(land));
        for (int i=0; i<m*n; i++)
            fa[i] = i;

        for (int i=0; i<positions.size(); i++) {
            island++;
            int x = positions[i][0], y = positions[i][1];
            int f = x * n + y;
            land[x][y] = true;
            for (int j=0; j<4; j++) {   // 4 direction check
                int tx = x + dr[j];
                int ty = y + dc[j];
                if (tx >=0 && tx < m && ty >= 0 && ty < n &&
                    land[tx][ty] && getfather(fa, tx*n+ty) != f) {
                    fa[getfather(fa, tx*n+ty)] = f;
                    island--;
                }
            }
            ret.push_back(island);
        }
        return ret;
    }

    // disjoint-set and path compression
    int getfather(int fa[], int i) {
        if (fa[i] == i) return i;
        return fa[i] = getfather(fa, fa[i]);
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章