当前位置:网站首页>并查集是什么?你还在为其烦恼?其实就是一个连通图的问题,理解起来没有那么困难

并查集是什么?你还在为其烦恼?其实就是一个连通图的问题,理解起来没有那么困难

2022-06-11 08:21:00 梵高的猪v

并查集

分析

并查集,一种特殊的数据结构(它的逻辑结构本质也是一颗“树”,有唯一的根节点,任意数的子节点),它的特殊在于它只定义了两种数据操作(查找和合并)。这是用来解决连通性问题,查找(find):就是查找任意两个节点是否连通,就是是否有共同的祖先(节点找它的父节点的过程,一层一层地找)。合并(union):把两个不同集合的节点合并在一起。
刚开始看到并查集这个词的时候,以为它是一个算法。其实也可以说是一个算法(对树问题的一种特殊操作方式),只不过这个算法的代码实现通常要定义两个函数:find()和union()

我定义了一个parent[n]数组,数组下标是每个节点的编号,存储的值是每个节点的父节点


这些联通的点又称为一个分组,find函数就是寻找节点,如果下标和值相同说明该节点是祖节点.

代码

public class UF {
    
    private int[] items;
    private int count;

    public UF(int n){
    
        this.count=n;
        items=new int[n];
        //规定初始化的时候索引下标即每个位置的值
        //这个值代表所在分组
        //也就是说初始化的时候n就代表分组个数
        for (int i = 0; i < n; i++) {
    
            items[i]=i;
        }
    }
    public int getCount(){
    
        return count;
    }

    //元素所在分组根标识
    public int getRootGroup(int p){
    
        while (p != items[p]) {
    
            p = items[p];
        }
        return p;
    }
    
    //判断两个点是否在同一分组中
    public boolean connected(int p,int q){
    
        return getRootGroup(p)==getRootGroup(q);
    }


    public void unioned(int p,int q){
    
        int pgroup = getRootGroup(p);
        int qgroup= getRootGroup(q);
        if (pgroup==qgroup){
    
            return;
        }
        items[pgroup]=qgroup;
        count--;
    }
}
class Test{
    
    public static void main(String[] args) {
    
        UF uf = new UF(5);
        System.out.println("分组前:"+uf.getCount());
        uf.unioned(1,2);
        System.out.println("分组后:"+uf.getCount());
    }
}

在前面的代码中,还存在一些问题,我们在一条可达链路的存储上使用树结构,这样遍历该链的时候就比直接遍历要好,如果要将两棵树合并,也比直接将两条链合并的高度要低.但是我们还可以优化,从前面的代码中可以看出,我们在合并的时候,其实并没有考虑到树的高度,就会很有造成树的高度是两棵树相加的结果,但是由于树的节点是分叉的,所以我们完全可以将矮的树合并到高的树当中,这样子最后树的高度就是最高的那颗树的了,而不是两棵树相加.
其实实现很简单,只需要在进行初始化数组的时候,将每个节点的高度保存一份就可以了:

    public void unioned(int p,int q){
    
        int pgroup = getRootGroup(p);
        int qgroup= getRootGroup(q);
        if (pgroup==qgroup){
    
            return;
        }
        //如果p的高度比q的高度低
        //那么就要高的作为根节点
        if (highs[pgroup]<highs[qgroup]){
    
            items[pgroup]=qgroup;
        }else
            items[qgroup]=pgroup;

        count--;
    }

畅通工程

image.png
请看上图,加入有20个城市,有七条已经修建好的路,下面的七组数据代表修建好的两个城市,问最后还得修建多少条道路才将所有城市联通?
这道问题就可以利用并查集巧妙解决,我们知道,如果并查集最后count的值为1,那么就代表该数组里只有一个分组,也就是所有节点都是联通的.
所以我们只需要将那七组数据调用unioned方法,合并成一组,之后将剩余的count-1就是要修建的路了.

        UF uf = new UF(20);
        System.out.println("分组前:"+uf.getCount());
        uf.unioned(0,1);
        uf.unioned(6,9);
        uf.unioned(3,8);
        uf.unioned(5,11);
        uf.unioned(2,12);
        uf.unioned(6,10);
        uf.unioned(4,8);
        int count=uf.getCount()-1;
        System.out.println("需要修建的路剩余:"+count); //12

原网站

版权声明
本文为[梵高的猪v]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_47303191/article/details/125109835