並查集(不相交集合)詳解與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));     //大小
    }
}

在這裏插入圖片描述

結語

  • 並查集屬於簡單可是很高效率的數據結構。在集合中常常會遇到。若是不採用並查集而傳統暴力效率過低,而不被採納。
  • 另外,並查集還普遍用於迷宮遊戲中,下面有機會能夠介紹用並查集實現一個走迷宮小遊戲。你們歡迎關注!
  • 最後,歡迎你們關注筆者公衆號,一塊兒學習、交流!筆者學習資源也放置公衆號和你們一塊兒分享!

相關文章
相關標籤/搜索