20230725-复习-并查集

45 阅读1分钟

面试题16.19. 水域大小

其实dfs更好写,但为了练并查集。

需要注意的是这道题并非朴素的并查集,而是需要维护每一簇节点的数量,而且这个数量的值仅在根节点是有意义的,其他非根节点因为没有额外维护(也不需要)是没有意义的。

class Solution {
public:
    int fa[1000 * 1000];
    int cnt[1000 * 1000];
    int dirs[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
    int getId(int i, int j, int n){
        return i * n + j;
    }
    void init(int size){
        for(int i = 0; i < size; ++i){
            fa[i] = i;
            cnt[i] = 1;
        }
    }

    int find(int x){
        return fa[x] == x ? x : (fa[x] = find(fa[x]));
    }

    void merge(int x, int y){
        int x_fa = find(x), y_fa = find(y);
        fa[x_fa] = y_fa;
        cnt[y_fa] += cnt[x_fa];
    }
    vector<int> pondSizes(vector<vector<int>>& land) {
        int m = land.size(), n = land[0].size();
        init(m * n);
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(land[i][j] == 0){
                    for(auto& dir : dirs){
                        int nx = i + dir[0], ny = j + dir[1];
                        if(nx < 0 || nx >= m || ny < 0 || ny >= n || land[nx][ny] != 0) continue;
                        if(find(getId(i, j, n)) != find(getId(nx, ny, n)))
                            merge(getId(i, j, n), getId(nx, ny, n));
                    }
                }
            }
        }
        vector<int> ans;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(land[i][j] == 0 && find(getId(i, j, n)) == getId(i, j, n)){
                    ans.push_back(cnt[getId(i, j, n)]);
                }
            }
        }
        sort(ans.begin(), ans.end());
        return ans;
    }
};