並查集

並查集,顧名思義,就是把元素併到一個集合裏,然後還可以查找某個元素在哪一個集合裏;


這其實就是並查集了,思想很簡單,而且很好寫,不過很少會有題專門考並查集,但是,不可否認的是,並查集是一個極爲有用的輔助算法,或者說是思想,再或者是一種實現方式;


並查集有幾個主要操作:

<1> 初始化:我們會把每一個點放入一個單獨的集合,即fa[x]=x,代表x所在的這個集合的代表元素是x;

<2> 查詢:我們每一個集合的表現形式是一顆樹,而所有的集合便表現爲一個森林,所以我們遞歸地查詢x所在的集合,一旦找到某一個點是一個集合的代表元素,那麼我們就可以認爲x與這個點在一個集合(而且一定在一個集合),並且這個集合是現在找到的這個點所代表的集合;

<3> 合併: 假設我們希望把元素A與元素B合併到一個集合中,那麼我們可以將這兩個點所在的集合的代表元素合併到一個集合,至於這個過程,詳情見代碼;

這裏寫圖片描述

聲明:本博客圖片來自網絡;

查詢:

int find(int x)                                                                  //查找我(x)的掌門
{
    int r=x;                                                                       //委託 r 去找掌門
    while (pre[r ]!=r)                                                        //如果r的上級不是r自己(也就是說找到的大俠他不是掌門 = =)
    r=pre[r ] ;                                                                   // r 就接着找他的上級,直到找到掌門爲止。
    return  r ;                                                                   //掌門駕到~~~
}

合併:

void join(int x,int y)                                                                   //我想讓虛竹和周芷若做朋友
{
    int fx=find(x),fy=find(y);                                                       //虛竹的老大是玄慈,芷若MM的老大是滅絕
    if(fx!=fy)                                                                               //玄慈和滅絕顯然不是同一個人
    pre[fx ]=fy;                                                                           //方丈只好委委屈屈地當了師太的手下啦
}

還有一個路徑壓縮,比較重要,它一定程度上決定了並查集的效率;

所謂路徑壓縮,就是把原來是一個長鏈的樹處理成一顆深度較淺的樹,以至於查詢時不至於遞歸太多次,其實就是一個小細節問題;

II find (R II x)
{
     if(x==fa[x]) return x;
      else return fa[x]=find(fa[x]); 
    //這個地方我們由 esle return find(fa[x]) 改爲了
    //return fa[x]=find(fa[x]);
    //就是每次查詢,我們就會把沒有並在代表元素上的節點併到代表元素上;
    //這樣就可以使得樹的深度較淺了;
} 

效果大概就是這樣的:

這裏寫圖片描述


然後依舊是完整代碼:

#include<iostream>
#include<cstdio>
#define II int
#define R register
#define I 123456
using namespace std;


II fa[I];

II n,m;


II find (R II x)
{
     if(x==fa[x]) return x;
      else return fa[x]=find(fa[x]); 
      //這個地方我們由 esle return find(fa[x]) 改爲了
      //return fa[x]=find(fa[x]);
      //就是每次查詢,我們就會把沒有並在代表元素上的節點併到代表元素上;
      //這樣就可以使得樹的深度較淺了;
} 


void join(R II x,R II y)
{
     fa[x]=y;
}


int main()
{
    scanf("%d%d",&n,&m);
    for(R II i=1;i<=n;i++) fa[i]=i;
     //初始化;
    for(R II i=1;i<=m;i++)
    {
        II x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(x==1) join(find(y),find(z));
        //注意一定要傳代表元素,保證正確性;
        //不明白可以畫圖試一下;
        else find(y)==find(z)?printf("Y\n"):printf("N\n");
        //查詢是否在一個集合中;
    }
    return 0;
}

by pretend-fal;

END;

發佈了33 篇原創文章 · 獲贊 13 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章