并查集的五种实现方法

并查集的五种实现方式

并查集可确定两个结点是否连接,合并两个结点,不考虑元素的内容用数组来保存。

接口实现:

public interface UF {
    /*
     * 并查集的接口
     * getsize 元素的个数
     * isConnected是否连接
     * union合并
     * */
    /*获得元素个数*/
    int getSize();

    /*p q 表示元素在数组中的索引
     * 判断p q 是否连接
     * */
    boolean isConnected(int p, int q);

    /*将p,q连接起来*/
    void union(int p, int q);
}

方法一:

public class UnionFindFirst implements UF{
    public int[] id;
    public int size;

    /*初始化
    * 为每一个元素(数组索引)创建一个集合(数组对应索引的值)
    * 属于集合表示
    * 1 2 3 4 5 6 7 8
    * 0 0 0 0 1 1 1 1
    * */
    public UnionFindFirst(int size) {
        this.size = size;
        id=new int[size];
        for (int i = 0; i < size; i++) {
            id[i]=i;
        }
    }
    public int find(int index){
        return id[index];
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p)==find(q);
    }

    @Override
    public void union(int p,int q) {
        int pNum=find(p);
        int qNum=find(q);
        if(pNum==qNum){
            return;
        }
        /*
        * 遍历;把qNum集合的值,全部变为pNum集合的值
        * 即把两个集合,融合为两个
        * */
        for (int i = 0; i < getSize(); i++) {
            if(id[i]==qNum){
                id[i]=pNum;
            }
        }
    }
}

方法二;

public class UnionFindSecond implements UF{
/*
* 将每个元素看做结点,节点之间相连接,形成树的结构
*eg:将结点1和结点3合并,将3和1的根节点相连接
* 1-->2 2-->2 3-->2
* */
    private int size;
    private int[] parents;

    public UnionFindSecond(int size) {
        this.size = size;
        parents=new int[size];
        for (int i = 0; i < size; i++) {
            parents[i]=i;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index){
        while(index!=parents[index]){
            index=parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p)==find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot=find(p);
        int qRoot=find(q);
        if(pRoot==qRoot){
            return;
        }
        parents[pRoot]=qRoot;
    }
}

方法三:

public class UnionFindThird implements UF {
    /*基于size进行优化,将结点少的挂在结点多的上边;
     *只修改union方法
     */

    int size;
    int[] parents;
    int[] nums;

    public UnionFindThird(int size) {
        this.size = size;
        parents = new int[size];
        nums = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
            nums[i] = 1;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            if (nums[pRoot] > nums[qRoot]) {
                parents[qRoot] = pRoot;
                nums[pRoot] += nums[qRoot];

            } else {
                parents[pRoot] = qRoot;
                nums[qRoot] += nums[pRoot];
            }
        }
    }
}

方法四:

public class UnionFindFourth implements UF {
    /*基于heigth进行优化,将高度低的树合并在高度高的树上;
     *只修改union方法
     */

    int size;
    int[] parents;
    int[] height;

    public UnionFindFourth(int size) {
        this.size = size;
        parents = new int[size];
        height = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
            height[i] = 1;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            if (height[pRoot] > height[qRoot]) {
                parents[qRoot] = pRoot;
            } else if(height[pRoot]<height[qRoot]){
                parents[pRoot] = qRoot;
            }else{
                parents[pRoot]=qRoot;
                height[qRoot]+=1;
            }
        }
    }
}

方法五:

public class UnionFindFifth implements UF {
    /*
     * 路径压缩
     * 方法:
     * parents[p]=parents[parents[p]];
     * 只需要改变find方法
     *
     * */
    int size;
    int[] parents;

    public UnionFindFifth(int size) {
        this.size = size;
        parents = new int[size];
        for (int i = 0; i < size; i++) {
            parents[i] = i;
        }
    }

    //查找索引为index的结点所在的根节点
    public int find(int index) {
        while (index != parents[index]) {
            parents[index]=parents[parents[index]];
            index = parents[index];
        }
        return index;
    }

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }

    /*将p所在的根节点指向q所在的根节点*/
    @Override
    public void union(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);
        /*判断节点的个数*/
        if (pRoot != qRoot) {
            parents[qRoot] = pRoot;
        }
    }
}

测试:

import java.util.Random;

public class UFTest {
    /**
     * @param uf 测试并查集
     * @param count 测试合并和是否连接的次数
     */
    public static void test(UF uf, int count) {
        long startTime = System.nanoTime();
        Random random = new Random();
        // 合并操作
        for (int i = 0; i < count; i++) {
            uf.union(random.nextInt(uf.getSize()), random.nextInt(uf.getSize()));
        }

        // 判断是否连接
        for (int i = 0; i < count; i++) {
            uf.isConnected(random.nextInt(uf.getSize()), random.nextInt(uf.getSize()));
        }
        long endTime = System.nanoTime();
        System.out.println("共用了" + (endTime - startTime) / 1000000000.0 + "s");
    }

    public static void main(String[] args) {
        int size = 10000;
        int count = 100000;
        UF uf1 = new UnionFindFirst(size);
        System.out.println("方法一:");
        test(uf1, count);
        UF uf2 = new UnionFindSecond(size);
        System.out.println("方法二:");
        test(uf2, count);
        UF uf3 = new UnionFindThird(size);
        System.out.println("方法二基于size的优化:");
        test(uf3, count);
        UF uf4 = new UnionFindFourth(size);
        System.out.println("方法二基于height的优化:");
        test(uf4, count);
        UF uf5 = new UnionFindFifth(size);
        System.out.println("方法二基于压缩路径的优化:");
        test(uf5, count);


    }
}

测试结果:

方法一:
共用了0.0900325s
方法二:
共用了1.0276614s
方法三基于size的优化:
共用了0.0335062s
方法四基于height的优化:
共用了0.0159129s
方法五基于压缩路径的优化:
共用了0.0136944s

你可能感兴趣的:(数据结构,java,算法,数据结构)