0
点赞
收藏
分享

微信扫一扫

并查集(优化:路径压缩、按秩合并)

晚熟的猫 2022-02-12 阅读 51

并查集可以用来解决一些元素分组问题,分为两种操作:
1、合并:把两个不相交的集合并为一个集合。
2、查询:查询两个元素是否在同一个集合中。

初始化:

class Solution {
	private static final int MAX = 500;
    private int fa[MAX]; // 存储每个节点的父节点

    public void init(int n) {
        for(int i = 1; i <= n; i++)
            fa[i] = i;
    }
}

查询:

    public int find(int x) {
        if(fa[x] == x)
            return x;
        else 
            return find(fa[x]);
    }

合并:

    public void merge(int i, int j) {
        fa[find(i)] = find(j);
    }

以上的查询和合并的效率低下,解决方法有路径压缩按秩合并

路径压缩(优化查询):

    public int find(int x) {
        if(x == fa[x])
            return x;
        else {
            fa[x] = find(fa[x]); // 把沿途的每一个节点的父节点都设为根节点
            return fa[x];
        }
    }

路径压缩优化后,并查集并不是始终都是一个菊花图(只有两层的树的俗称),因为路径压缩只在查询时进行,也只压缩一条路径,所以并查集最终的结构仍然可能时比较复杂的。

按秩合并(优化合并):
我们用一个数组rank[]来记录每个根节点对应的树的深度(如果不是根节点,其rank相当于以它作为根节点的子树的深度)。初始化时把所有元素的rank设为1,合并时比较两个根节点,把rank较小者往较大者上合并。这样合并可以避免合并后的树的深度变长,从而不会使查询时间变长。

    public void init(int n) {
        for(int i = 0; i < n; i++) {
            fa[i] = i;
            rank[i] = 1; // 初始化每一个节点的rank为1
        }
    }
    public void merge(int i, int j) {
        int x = find(i), y = find(j);
        if(rank[x] <= rank[y])
            fa[x] = y;
        else
            fa[y] = x;
        if(rank[x] == rank[y] && x != y)
            rank[y]++; // 如果深度相同且根节点不同,则新的根节点深度+1
    }
举报

相关推荐

0 条评论