并查集

when ? how ? what ? why?

what

什么是并查集?

how

并查集问题中集合存储如何实现?

并查集的操作


什么是并查集?

集合的合并、查询某元素属于什么集合。

并查集问题中集合存储如何实现?

可以用树结构表示集合,树的每个结点代表一个集合元素

集合

S1={1,2,3,4},S2={5,6,7},S3={8,9,10}

使用双亲表示法:孩子指向双亲

并查集的操作

使用数组来存储,数组对应的下标来表示元素的值,数组中的值表示父亲结点。

0 下标不存放元素,-1 表示为根。大于 0 的值表示父亲结点元素。

初始化

将数组中的所有元素赋值为 -1。

public static void init(int[] setData) {
    for (int i = 1; i < setData.length; i++) {
        setData[i] = -1;
    }
}

查找

查找某元素对应集合的根结点的值

public static int find(int[] setData, int x) {
    for (; setData[x] > 0; x = setData[x]) {
    }
    return x;
}

合并

先查找两个元素对应集合根结点是否相同,如果相同就不用合并。

public static void union(int[] setData,int numA,int numB){
    int rootA=find(setData,numA);
    int rootB=find(setData,numB);
    if(rootA!=rootB){
        setData[rootB]=rootA;
    }
}

问题?

将 1,2,3,4,5 合并后

那么查找的时间复杂度 O(N)

通过按秩合并或路径压缩来优化。

按秩合并

按规模或按高度进行合并的算法,就统称按秩合并

1.将矮树合并到高树上

将原本根结点 -1 对应的值改为 - 树高

setData[root] = - 树高

 public static void union(int[] setData,int numA,int numB){
    int rootA=find(setData,numA);
    int rootB=find(setData,numB);
    if(setData[rootA]<setData[rootB]){
        setData[rootB]=rootA;
    }else{
        if(setData[rootA]==setData[rootB]){
            setData[rootB]--;
        }
        setData[rootA]=rootB;
    }
}

2.将规模小的树合并到规模大的树

将原本根结点 -1 对应的值改为 - 树的规模

setData[root] = - 树的结点数

public static void union(int[] setData, int numA, int numB) {
    int rootA = find(setData, numA);
    int rootB = find(setData, numB);
    if (rootA != rootB) {
        if (setData[rootA] < setData[rootB]) {
            setData[rootA] = setData[rootA] + setData[rootB];
            setData[rootB] = rootA;
        } else {
            setData[rootB] = setData[rootB] + setData[rootA];
            setData[rootA] = rootB;
        }
    }
}

路径压缩

public  static int find(int[] setData,int x){
    if(setData[x]<0){
        //找到根结点
        return x;
    }else {
        return setData[x] = find(setData, setData[x]);
    }
}

调用 find(setData,5) 后 树结构如下图,当第二次查询结点 5 时只需要2次比较就可以,这样大大提高效率。

总结

参考

数据结构 陈越 何钦铭

主要是用树表示集合,使用双亲表示法,操作集合的合并、查询元素所在的集合,以及通过按秩合并来优化集合的合并、路径压缩来优化查找。

有什么问题欢迎指出来,十分感谢!

posted @ 2018-07-13 16:02  罗贱人  阅读(244)  评论(0编辑  收藏  举报