并查集学习

什么是并查集

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

并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根,就能确定它在哪个集合里。

百度百科

并查集包括两种操作:

  1. find(x) 查询元素所属的集合
  2. union(x, y) 合并两个不相关的集合

我理解的并查集,就是对于一系列元素,不同元素组成不同的集合(使用树的形式描述集合),多个集合共同构成并查集(森林),提供集合与集合的合并(两棵的合并)和 查找元素所属的集合(在哪棵树)

如何实现并查集

并查集中只关注元素属于哪个集合,集合之间的合并操作,以树形式表示集合,每个元素只需要知道自己所属树的根节点就能知道自己属于哪个集合(属于哪个树),集合合并也可转化为树的合并,因此可使用类似于完全二叉树的数组存储方式实现并查集。

存储结构实现:

  • 使用father数组,存储元素的父节点(不直接存储根节点的原因在于,合并操作无法保证该性质)

  • 每个元素初始化父节点为本身,表示并查集初始每个元素自成一个集合

    int father[elements];

操作实现:

  1. find(x) 查询元素所属集合

    // 不断的向上查询父节点,直到找到当前集合的根
    int find(x){
    	root = x;
    	while(father[root] != root){
    		parent = father[root];
    	}
    	return root;
    }
    
  2. union 合并集合操作
    找到两个集合的根节点,将其中一个根节点父节点设置为另一集合根节点

    int uninon(x,y){
    	int root_x = find(x)
    	int root_y = find(y)
    	// 以x插入到y为例
    	parent[root_x] = root_y
        return root_y
    }
    
  • java实现

    public class DisjointSetUnion {
        private int[] parent;
        public DisjointSetUnion(int nums){
            parent = new int[nums];
            //初始化
            for(int i = 0;i < nums;i++){
                parent[i] = i;
            }
        }
        public int find(int x){
            while(parent[x] != x){
                x = parent[x];
            }
            return x;
        }
        public int union(int x,int y){
            int parent_x = find(x);
            int parent_y = find(y);
            parent[parent_x] = parent_y;
            return parent_y;
        }
    }
    
并查集优化

待补充

并查集应用
  1. 洛谷p1551 亲戚问题
    比较简单的并查集思路,求是否具有亲戚关系,即判断两元素是否在一个集合,java代码实现:

    import java.util.Scanner;
    class Main{
        static int[] parent;
    
        public static int find(int x){
            while(parent[x] != x){
                x = parent[x];
            }
            return x;
        }
    
        public static int union(int x,int y){
            int parent_x = find(x);
            int parent_y = find(y);
            parent[parent_x] = parent_y;
            return parent_y;
        }
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int n,m,p;
            n = scanner.nextInt();
            m = scanner.nextInt();
            p = scanner.nextInt();
    
            //初始化并查集
            parent = new int[n];
            for(int i = 0;i < parent.length;i++){
                parent[i] = i;
            }
            //读取关系合并并查集
            for(int i = 0;i < m;i++){
                union(scanner.nextInt() - 1,scanner.nextInt() - 1);
            }
            //判断两个集合是否在同一个集合中
            for(int i = 0;i < p;i++){
                if(find(scanner.nextInt() - 1) == find(scanner.nextInt() - 1)){
                    System.out.println("Yes");
                }
                else {
                    System.out.println("No");
                }
            }
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值