[LeetCode305]Number of Islands II

Hard..

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:

Given m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]].
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
We return the result as an array: [1, 1, 2, 3]

Challenge:

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

Hide Company Tags Google
Hide Tags Union Find
Hide Similar Problems (M) Number of Islands

我好讨厌union find啊。。。

这道题还要继续理解一下, 等我懂了再来说一说。

class UnionFind2D {
public:
    UnionFind2D(int m, int n) {
        for (int i = 0; i < m * n; i++) ids.push_back(-1);
        for (int i = 0; i < m * n; i++) szs.push_back(1);
        M = m, N = n, cnt = 0;
    }
    int index(int x, int y) {
        return x * N + y;
    }
    int size(void) {
        return cnt;
    }
    int id(int x, int y) {
        if (x >= 0 && x < M && y >= 0 && y < N)
            return ids[index(x, y)];
        return -1;
    }
    int add(int x, int y) {
        int idx = index(x, y);
        ids[idx] = idx;
        szs[idx] = 1;
        cnt++;
        return idx;
    }
    int root(int i) {
        for (; i != ids[i]; i = ids[i])
            ids[i] = ids[ids[i]];
        return i;
    }
    bool find(int p, int q) {
        return root(p) == root(q);
    }
    void unite(int p, int q) {
        int i = root(p), j = root(q);
        if (szs[i] > szs[j]) swap(i, j);
        ids[i] = j;
        szs[j] += szs[i];
        cnt--;
    }
private:
    vector<int> ids, szs;
    int M, N, cnt;
};

class Solution {
public:
    vector<int> numIslands2(int m, int n, vector<pair<int, int>>& positions) {
        UnionFind2D islands(m, n);
        vector<pair<int, int>> dirs = { { 0, -1 }, { 0, 1 }, { -1, 0 }, { 1, 0 } };
        vector<int> ans;
        for (auto& pos : positions) {
            int x = pos.first, y = pos.second;
            int p = islands.add(x, y);
            for (auto& d : dirs) {
                int q = islands.id(x + d.first, y + d.second);
                if (q >= 0 && !islands.find(p, q))
                    islands.unite(p, q);
            }
            ans.push_back(islands.size());
        }
        return ans;
    }
};

12/15/2015 update:

上面那个code 是path compression的( 2116 ms),自己写了个没有path compression的能过,但很慢。。2504 ms
基本思想就是把2d展开成1d每个点都有自己的idx,然后利用普通的union find做。。。就是找root, root不一样就可以合并并且减少cnt初始值,(隐含着root一样已经在一个group里了,不需要减少cnt值。。) 因为此时我们cnt的初始值设置的是:假设当前island跟之前所有的都是独立的,就是res.back()+1. 记得update roots。

code 如下:

class Solution {
public:
    int findRoot(int i, vector<int>& roots){
        if(roots[i] == i) return i;
        return findRoot(roots[i], roots);
    }

    vector<int> numIslands2(int m, int n, vector<pair<int, int>>& positions) {
        vector<int> res;
        vector<int> roots(m*n, -1);
        vector<pair<int, int>> dirs = {{1,0}, {-1,0}, {0,1}, {0,-1}};
        for(auto p : positions){
            int x = p.first, y = p.second;
            int idx = x * n + y;
            roots[idx] = idx;
            int cnt = res.empty() ? 1 : res.back() + 1;//initial islands number as individual one!
            for(auto d : dirs){
                int newX = x + d.first, newY = y + d.second;
                int newIdx = newX * n + newY;
                if(newX >=0 && newX < m && newY >=0 && newY < n && roots[newIdx] != -1){//only try to union island, -1 means water
                    int root1 = findRoot(newIdx, roots);
                    int root2 = idx;
                    if(root1 != root2) --cnt;// current island can connect with its neighbors.
                                                //if root1 == root2 means they already in same group we dont need decrease the cnt.
                    roots[root1] = root2;
                }
            }
            for(int i = 0; i<roots.size(); ++i){
                    cout<<i<<"=>"<<roots[i]<<",";
                }
                cout<<endl;
            res.push_back(cnt);
        }
        return res;
    }
};

再贴一下这个slides:
https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf

还有time complexity 分析:
啊啊啊啊为什么我不会啊!烦躁
https://leetcode.com/discuss/69392/python-clear-solution-unionfind-class-weighting-compression

你可能感兴趣的:(LeetCode)