编程-并查集

 

初始时,有 N 个朋友圈,即每个人一个圈子。

如果两个人是朋友,则进行一次 merge,如果merge成功,则朋友圈数量减一。

 

std::vector<int> fa;
void init(int N) {
    fa.resize(N);
    for (int i = 0; i < N; i++) {
        fa[i] = i;
    }
}
int find(int x) {
    int r = x;
    while(fa[r] != r) {
        r = fa[r];
    }
    while(fa[x] != x) {
        int t = fa[x];
        fa[x] = r;
        x = t;
    }
    return x;
}
bool merge(int u, int v) {
    int ru = find(u);
    int rv = find(v);
    if (ru == rv) {
        return false;
    }
    fa[ru] = rv;
    return true;
}
class Solution {
public:
    int findCircleNum(vector<vector<int>>& M) {
        int N = M.size();
        init(N);
        int anw = N;
        for (int i = 0; i < N; i++) {
            for (int j = i+1; j < N; j++) {
                if (M[i][j] && merge(i, j)) {
                    anw --;
                }
            }
        }
        return anw;
    }
};

 

本文将呈现如下内容:
  • 一个在实际工作中遇到的问题
  • 用 C++ 实现一个并查集
  • 路径压缩

 

 

 

 

 

547. 省份数量

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

 

示例 1:


输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:


输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
 

提示:

1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j] 为 1 或 0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]

 

class Solution {
public:
    vector<int> p;
    // 路径压缩
    int find(int x){
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        // 初始化
        for(int i = 0; i < n; i ++)  p.push_back(i);
        int cnt = n;
        for(int i = 0; i < n; i ++)
            for(int j =0; j < i; j++)
                if(isConnected[i][j] && find(i) != find(j)){
                    p[find(i)] = find(j);  // 集合合并
                    cnt --;
                }

        return cnt;
    }
};

 

姨的解法

 

200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

 

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0' 或 '1'

 

class UF {
public:
    vector<int> parent;
    int n;

    UF(int _n) : n(_n), parent(_n) {
        iota(parent.begin(),parent.end(),0);
    }

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

    void merge(int x, int y) {
        x = find(x);
        y = find(y);
        if(x == y) return;
        parent[x] = y;
        n--;
    }

    bool isConnected(int x, int y) {
        return find(x) == find(y);
    }

    int getSize() {
        return n;
    }
};
class Solution {
public:
    int dx[4] = {-1,1,0,0}; 
    int dy[4] = {0,0,-1,1};
    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size();
        int n = m == 0 ? 0 : grid[0].size();
        if(m == 0 || n == 0) return 0;
        vector<int> cnt(2,0);
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++) 
                cnt[grid[i][j]-'0']++;
        // (i,j) -> index = i*n + j
        UF uf(m*n);
        for(int i=0;i<m;i++) {
            for(int j=0;j<n;j++) {
                if(grid[i][j] == '0')
                    continue;
                for(int k=0;k<4;k++) {
                    int xx = i+dx[k], yy = j+dy[k];
                    if(xx < 0 || xx >= m || yy < 0 || yy >= n)
                        continue;
                    if(grid[xx][yy] == '1')
                        uf.merge(i*n+j, xx*n+yy);
                }
            }
        }

        return uf.getSize() - cnt[0];
    }
};

 

 

const int N = 100010;
class Solution {
public:
    int p[N];
    int find(int x) {
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size(), n = grid[0].size();
        for(int i = 0; i < m; i ++) {
            for(int j = 0; j < n; j ++) {
                p[i * n + j] = i * n + j;
            }
        }
        int res = 0;
        int dx[4] = {-1, 0 , 1, 0}, dy[4] = {0, -1, 0, 1};
        for(int i = 0; i < m * n; i ++) {
            int prex = i / n, prey = i % n;
            if(grid[prex][prey] == '1') {
                res ++;
                for(int j = 0; j < 4; j ++) {
                    int ux = prex + dx[j], uy = prey + dy[j];
                    if(ux >= 0 && ux < m && uy >= 0 && uy < n && grid[ux][uy] == '1') {
                        int u = ux * n + uy;
                        int pi = find(i), pu = find(u);
                        if(pi != pu) {
                            p[pu] = pi;
                            res --;
                        }
                    }
                }
            }
        }
        return res;
    }
};

 

 

经典解法

 

posted @ 2021-09-02 10:15  aaronwell  阅读(48)  评论(0)    收藏  举报