算法扩充知识-并查集

背景:若一个家族过于庞大,要判断两个人是否是亲戚,不太容易。现规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x和y是亲戚,那么x的亲戚都是y的亲戚y的亲戚也都是x的亲戚。

简介:并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。

主要操作:

1、初始化。把每个点所在集合初始化为其自身。

2、查找。

(1)查找时,采用递归的方法找其祖宗,祖宗集合号等于自己时停止。

(2)在回归时,把当前结点祖宗路径上的所有结点统一为祖宗的集合号

3、合并。如果两个元素的集合号不同,将两个元素合并为一个集合。擒贼先擒王,只改祖宗即可。

图解:

假设现在有7个人,通过输入亲戚关系图,判断两个人是否有亲戚关系

(1)初始化

集合号初始化
祖宗关系图

(2)输入亲戚关系2和7

(3)查找

查找2所在的集合号为2,7所在的集合号为7。

(4)合并

两个元素集合号不同,将两个元素合并为一个集合。在此约定把小的集合号赋值给大的集合号,因此修改father[7]=2。

更新后结果

查找方法:

关系图

判断5和2是不是亲戚关系:

(1)查找5的父亲2,2的父亲1,1的父亲是1,搜索停止。5到其祖宗1这条路径上所有的结点集合号更新为1。 

(2)查找2的父亲1,1的父亲是1,搜索停止。2到其祖宗1这条路径上所有的结点集合号更新为1。

(3)5和2的集合号都为1,所以5和2是亲戚关系。

Java代码实现:

public class WeightedQuickUnionUF {

    private int[] id;
    private int[] sz; //元素代表,某一家族成员数量
    private int count;

    public WeightedQuickUnionUF(int N) {
        count = N; //初始有N的家族
        id = new int[N];
        for (int i = 0; i < N; i++) id[i] = i;
        sz = new int[N];
        for (int i = 0; i < N; i++) sz[i] = 1;
    }

    public int count() {
        return count;
    }

    private int root(int i) {
        while (i != id[i]) {
            id[i] = id[id[i]]; //将子孙i指向的爸爸改为指向爷爷
            i = id[i]; //子孙i更新为了爷爷i,
        }
        return i;
    }

    public boolean connected(int p, int q) {
        return root(p) == root(q);
    }

    public void union(int p, int q) {
        int i = root(q);
        int j = root(p);
        if (i == j) return;

        //Make smaller root point to larger one.
        //使小家族指向大家族,若家族j比家族i大
        if (sz[i] < sz[j]) {
            id[i] = j; //家族i指向家族j,叫家族j爸爸
            sz[j] += sz[i];  //家族j的成员数量加上了家族i的成员数量
        } else {
            id[j] = i;
            sz[i] += sz[j];
        }
        count--; //合并之和,家族种类数减1
    }
}

猜你喜欢

转载自blog.csdn.net/qq_27437197/article/details/86998420
今日推荐