并查集


1. 概述

并查集用来解决图的连通性问题,并查集的方法首先要为每一个点建立集合,
接着写出判断两个点是否属于一个集合的方式,最后不断合并集合

  • Union -- 连接两个节点
  • Find -- 查找所属的连通分量
    所以,并查集主要就是实现以下接口
class UF {
    /* 将 p 和 q 连接 */
    public void union(int p, int q);
    /* 判断 p 和 q 是否连通 */
    public boolean connected(int p, int q);
    /* 返回图中有多少个连通分量 */
    public int count();
    
    /* 返回当前节点的根节点 */
    private int find(int x);
}

为了便于描述连通状态(同时使图的关系可以用数组存储)
让同属一个连通域的元素指向同一根节点
这里可以用数组parent[]表示这种关系

  • 如果自己就是根节点,那么 parent[i] = i,即自己指向自己
  • 如果自己不是根节点,则 parent[i] = root 指向根节点索引
private int count;
private int[] parent;
// 构造函数
public UF (int n) {
    this.count = n; //集合总数
    parent = new int[n]; //初始化指针(索引)
    for (int i = 0; i < n; i++) {
        // 最初,每个节点均是独立的
        parent[i] = i; //每个节点指针指向自己
    }
}

Union 方法

// 伪代码
public void union(int p, int q) {
    int rootP = find(p);
    int rootQ = find(q);
    if (rootP == rootQ) return;
    parent[rootP] = rootQ; //集合p合并到集合q中
    count--;//连通域减一
}

Find方法(路径压缩后)

private int find(int x) {
    while (x != parent[x]) {//遍历求根节点
        parent[x] = parent[parent[x]];//重构节点,其实该操作只会进行一次,因为每次合并都会更新,不会使高度过深
        x = parent[x];}
    return x;
}

private int find(int x) {
    if(parent[x]==x) reuturn x;
    parent[x] = find(parent[x]);//递归求根节点,同时重构该集合,减少之后查找次数
    return parent[x];
}

2. 最长连续序列

最长连续序列
class Solution {
public:
    unordered_map series;//指针关系
    unordered_map count;//用于合并时计数
    int max_ = 1;
    int longestConsecutive(vector& nums) {
       if(nums.size()==0) return 0;
       for(int num:nums){
            series[num]=num;//每个数建立一个集合
            count[num]=1;//初始值为1
            }
        for(int i = nums.size()-1; i>=0; i--)
            if(series.count(nums[i]+1))   union_(nums[i]);

        return max_;
    }
    void union_(int num){
        int right1 = find(num);
        int right2 = find(num+1);
        if(right1==right2) return;
        series[num]=right2;//合并集合
        count[right2]=count[right1]+count[right2];//合并计数
        max_ = max(max_,count[right2]);//记录最大值
    }
    int find(int num){
        if(series[num]==num) return num;
        series[num] = find(series[num]);//压缩路径
        return series[num];
    }
};

3. 岛屿数量

岛屿数量
class Solution {
public:
    vector parent;
    int sum;
    int numIslands(vector>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        int index;
        //初始化parent数组,记录初始岛屿数量
        for (int i = 0; i < m; i++)
            for(int j = 0; j < n; j++){
                index = i * n + j;
                parent.push_back(index);//后面根据索引建立集合簇
                if(grid[i][j] == '1')
                    sum++;  //记录初始岛屿数量
            }
        
        for (int i = 0; i < m; i++) 
            for (int j = 0; j < n; j++){
                index = i * n + j;  //从左往右,从上往下遍历合并全部岛屿
                if (grid[i][j] == '1') {
                    if (i+1 < m && grid[i+1][j] == '1') { //下
                        unions(index, (i + 1) * n + j);
                    }
                    if (j+1