其他分享
首页 > 其他分享> > 并查集

并查集

作者:互联网

1. 简介

  1. 并查集(Union Find)也叫做不相交集合
  2. 并查集有两个核心操作
    1. 查找(Find):查找元素所在集合
    2. 合并(Union):将两个元素所在集合合并为一个集合
  3. 两种常见实现思路
    1. QuickFind:查找 —— O(1),合并 —— O(n)
    2. QuickUnion(常用):查找 —— O(logn)可优化至O(5),合并 —— O(logn)可优化至O(5)

2. 并查集如何存储数据

以存储整形数据为例,可以用数组实现并查集。比如数组的索引表示数据,值代表所属集合。
、

3. 实现

3.1 初始化

	private int[] parents; 
    public UnionFind(int capacity){
        parents = new int[capacity];
        //每个数据单独属于一个集合
        for(int i=0;i<parents.length;i++){
            parents[i] = i;
        }
    }

3.2 基于QuickFind的Union、Find操作

在这里插入图片描述

union(1,0)是指将1的结点改为0的父结点,即将1所在集合并入0所在集合。

    public void union(int v1, int v2){
        int p1 = find(v1);
        int p2 = find(v2);
        if(p1 == p2){
            return;
        }
        for (int i = 0; i < parents.length; i++) {
            if(parents[i] == p1){
                parents[i] = p2;
            }
        }
    }
    
    public int find(int v){
        return parents[v];
    }

这种情况下实现的并查集,其相应的树的高度最多为2,所以查找时间短

3.3 基于QuickUnion

3.3.1 Union、Find操作

在这里插入图片描述
union(1,0)是指将1的结点改为0的父结点,即将1所在集合并入0所在集合。

	public void union(int v1, int v2){
        int p1 = find(v1);
        int p2 = find(v2);
        if(p1 == p2){
            return;
        }
        parents[p1] = p2;
    }
    
    public int find(int v){
        while(v != parents[v]){
            v = parents[v];
        }
        return v;
    }
3.3.2 Union、Find操作的优化
  1. 优化点:在合并过程中可能会出现树不平衡的状况,甚至退化成链表
    在这里插入图片描述

  2. 方案

    1. 基于size:元素少的树 嫁接到 元素多的树
    	int[] sizes = new int[capacity];
    	Arrays.fill(sizes,1);
    	
    	public void union_s1(int v1, int v2){
            int p1 = find(v1);
            int p2 = find(v2);
            if(p1 == p2){
                return;
            }
            if(sizes[p1] < sizes[p2]){
                parents[p1] = p2;
                sizes[p2] += sizes[p1];
            }else{
                parents[p2] = p1;
                sizes[p1] += sizes[p2];
            }
        }
    
    1. 基于rank: 矮的树 嫁接到 高的树
    	int[] ranks= new int[capacity];
    	Arrays.fill(ranks,1);
        public void union_s2(int v1, int v2){
            int p1 = find(v1);
            int p2 = find(v2);
            if(p1 == p2){
                return;
            }
            if(ranks[p1] < ranks[p2]){
                ranks[p1] = p2;
            }else if(ranks[p1] > ranks[p2]){
                ranks[p2] = p1;
            }else{
                parents[p1] = p2;
                ranks[p2]++;
            }
    	}
    
  3. 路径压缩

    1. 随依然有了基于rank的优化,树相对会平衡些,但随着Union的次数增多,树的高度依然会越来越高,这就导致了find操作时间复杂度变高
    2. 使用路径压缩可解决这一问题。路径压缩是指,在find()时使路径上的所有结点都指向根节点,从而降低树的高度
    public int find_s(int v){
        if(parents[v] != v){
            parents[v] = find(parents[v]);
        }
        return parents[v];
    }
    
  4. 更优的做法
    虽然路径压缩可以降低树的高度,但是由于调用了递归,在优化树的高度过程中依然会付出一些代价。接下来介绍的两种方法,在降低树的高度的同时有着更小的代价。

    1. 路径分裂:是路径上的每个结点都指向其祖父节点
      在这里插入图片描述
        public int find_s1(int v){
            while(v != parents[v]){
                int p = parents[v];
                parents[v] = parents[p];
                v = p;
            }
            return v;
        }
    
    1. 路径减半:是路径上每隔一个结点就指向其祖父节点
      在这里插入图片描述
        public int find_s2(int v){
            while(v != parents[v]){
                parents[v] = parents[parents[v]];
                v = parents[v];
            }
            return v;
        }
    

3.4 isSame操作,判断是否属于同一集合

    public boolean isSame(int v1, int v2){
        int p1 = find(v1);
        int p2 = find(v2);
        return p1==p2;
    }

标签:p2,p1,int,查集,public,parents,find
来源: https://blog.csdn.net/qq_42451178/article/details/113448044