并查集(不相交集合)详解与java实现

@(文章目录)数组

认识并查集

对于并查集(不相交集合),不少人会感到很陌生没听过或者不是特别了解。实际上并查集是一种挺高效的数据结构。实现简单,只是全部元素统一听从一个规律因此让办事情的效率高效起来。数据结构

对于定意义,百科上这么定义的:函数

并查集,在一些有N个元素的集合应用问题中,咱们一般是在开始时让每一个元素构成一个单元素的集合,而后按必定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪一个集合中。其特色是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,每每在空间上过大,计算机没法承受;即便在空间上勉强经过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题须要的结果,只能用并查集来描述。学习

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。经常在使用中以森林来表示。this

并查集解析

基本思想

  • 初始化,一个森林每一个都为独立。一般用数组表示,每一个值初始为-1。各自为根
    在这里插入图片描述
  • join(a,b) 操做。a,b两个集合合并。注意这里的a,并非a,b合并,而是a,b的集合合并。这就派生了一些状况:
  • a,b若是是独立的(没有和其余合并),那么直接a指向b(或者b指向a),即data[a]=b;同时为了表示这个集合有多少个,本来-1的b再次-1.即data[b]=-2.表示以b为父亲的节点有|-2|个。
    在这里插入图片描述
    在这里插入图片描述
  • a,b若是有集合(可能有父亲,可能本身是根),那么咱们固然不能直接操做a,b(由于a,b可能已经指向别人了.)那么咱们只能操做a,b的祖先。由于a,b的祖先是没有指向的(即数据为负值表示大小)。那么他们首先一个负值要加到另一个上面去。另外这个数值要变成指向的那个表示联系。
    在这里插入图片描述

对于上述你可能会有疑问:3d

如何查看a,b是否在一个集合?

  • 查看是否在一个集合,只须要查看节点根祖先的结果是否相同便可。由于只有根的数值是负的,而其余都是正数表示指向的元素。因此只须要一直寻找直到不为正数进行比较便可

a,b合并,到底是a的祖先合并在b的祖先上,仍是b的祖先合并在a上?

  • 这里会遇到两种状况,这个选择也是很是重要的。你要弄明白一点:树的高度+1的化那么整个元素查询的效率都会下降!

因此咱们一般是:小数指向大树(或者低树指向高树),这个使得查询效率可以增长!
在这里插入图片描述
固然,在高度和数量的选择上,还须要你本身选择和考虑。code

其余路径压缩?

每次查询,自下向上。当咱们调用递归的时候,能够顺便压缩路径,由于咱们查找一个元素其实只须要直到它的祖先,因此当他距离祖先近那么下次查询就很快。而且压缩路径的代价并不大!
在这里插入图片描述blog

代码实现

并查集实现起来较为简单,直接贴代码!递归

package 并查集不想交集合;

import java.util.Scanner;
public class DisjointSet {
    static int tree[]=new int[100000];//假设有500个值
    public DisjointSet()    {set(this.tree);}
    public DisjointSet(int tree[]) 
    {
        this.tree=tree;
        set(this.tree);
    }
    public void set(int a[])//初始化全部都是-1 有两个好处,这样他们指向-1说明是本身,第二,-1表明当前森林有-(-1)个
    {
        int l=a.length;
        for(int i=0;i<l;i++)
        {
            a[i]=-1;
        }
    }
    public int search(int a)//返回头节点的数值
    {
        if(tree[a]>0)//说明是子节点
        {
            return tree[a]=search(tree[a]);//路径压缩
        }
        else
            return a;
    }
    public int value(int a)//返回a所在树的大小(个数)
    {
        if(tree[a]>0)
        {
            return value(tree[a]);
        }
        else
            return -tree[a];
    }
    public void union(int a,int b)//表示 a,b所在的树合并
    {
        int a1=search(a);//a根
        int b1=search(b);//b根
        if(a1==b1) {System.out.println(a+"和"+b+"已经在一棵树上");}
        else {
        if(tree[a1]<tree[b1])//这个是负数,为了简单减小计算,不在调用value函数
        {
            tree[a1]+=tree[b1];//个数相加  注意是负数相加
            tree[b1]=a1;       //b树成为a的子树,直接指向a;
        }
        else
        {
            tree[b1]+=tree[a1];//个数相加  注意是负数相加
            tree[a1]=b1;       //b树成为a的子树,直接指向a;
        }
        }
    }
    public static void main(String[] args)
    {       
        DisjointSet d=new DisjointSet();
        d.union(1,2);
        d.union(3,4);
        d.union(5,6);
        d.union(1,6);
        
        d.union(22,24);
        d.union(3,26);
        d.union(36,24);
        System.out.println(d.search(6));    //头
        System.out.println(d.value(6));     //大小
        System.out.println(d.search(22));   //头
        System.out.println(d.value(22));     //大小
    }
}

在这里插入图片描述

结语

  • 并查集属于简单可是很高效率的数据结构。在集合中常常会遇到。若是不采用并查集而传统暴力效率过低,而不被采纳。
  • 另外,并查集还普遍用于迷宫游戏中,下面有机会能够介绍用并查集实现一个走迷宫小游戏。你们欢迎关注!
  • 最后,欢迎你们关注笔者公众号,一块儿学习、交流!笔者学习资源也放置公众号和你们一块儿分享!

相关文章
相关标签/搜索