Union-Find Set 并查集

v2-b87b6b4929022f7581ccd6e58df4a1fc_r.png

![TOC]

并查集操作的復(fù)雜度是log n。是一個(gè)衰減非常快的函數(shù)慌洪,即使n 很大,log n的結(jié)果也接近一個(gè)常數(shù)凑保,不會(huì)超過5冈爹。

其實(shí)并查集顧名思義就是有“合并集合”和“查找集合中的元素”兩種操作的關(guān)于數(shù)據(jù)結(jié)構(gòu)的一種算法。

A case of its application

有n個(gè)人欧引,每個(gè)人都有唯一的標(biāo)簽频伤,分別是0,1,……,n – 1。已知0號(hào)得了一種傳染病芝此,這種病只要與人接觸了就會(huì)傳染憋肖,一傳十十傳百。我們統(tǒng)計(jì)了哪些人有過接觸婚苹,求最終被感染的人是哪些岸更?

假設(shè)我們給出的接觸信息如下:

[[0, 1], [0, 2], [1, 3], [3, 4], [1, 2], [5, 4]]

先合并

image.png

最后查找0所在的集合

Other applications

2、用在求解最小生成樹的Kruskal算法里租副。

初始化坐慰、查找、合并

初始化存儲(chǔ)空間(array or struct)

一般來說用僧,題目簡(jiǎn)單用數(shù)組结胀,題目復(fù)雜用結(jié)構(gòu)體,因?yàn)榻Y(jié)構(gòu)體有條理责循,數(shù)組可以少打幾個(gè)字糟港。

包括對(duì)所有單個(gè)的數(shù)據(jù)建立一個(gè)單獨(dú)的集合(即根據(jù)題目的意思自己建立的最多可能有的集合,為下面的合并查找操作提供操作對(duì)象)
在每一個(gè)單個(gè)的集合里面院仿,有三個(gè)東西秸抚。
1,集合所代表的數(shù)據(jù)歹垫。(這個(gè)初始值根據(jù)需要自己定義剥汤,不固定)
2,這個(gè)集合的層次通常用rank表示(一般來說排惨,初始化的工作之一就是將每一個(gè)集合里的rank置為0)吭敢。
3,這個(gè)集合的類別parent(有的人也喜歡用set表示)(其實(shí)就是一個(gè)指針暮芭,用來指示這個(gè)集合屬于那一類鹿驼,合并過后的集合,他們的parent指向的最終值一定是相同的辕宏。)
(**有的簡(jiǎn)單題里面集合的數(shù)據(jù)就是這個(gè)集合的標(biāo)號(hào)畜晰,也就是說只包含2和3,1省略了)瑞筐。
初始化的時(shí)候凄鼻,一個(gè)集合的parent都是這個(gè)集合自己的標(biāo)號(hào)。沒有跟它同類的集合聚假,那么這個(gè)集合的源頭只能是自己了块蚌。
(最簡(jiǎn)單的集合就只含有這三個(gè)東西了,當(dāng)然魔策,復(fù)雜的集合就是把3指針這一項(xiàng)添加內(nèi)容匈子,如PKU食物鏈那題,我們還可以添加enemy指針闯袒,表示這個(gè)物種集合的天敵集合虎敦;food指針,表示這個(gè)物種集合的食物集合政敢。隨著指針的增加其徙,并查集操作起來也變得復(fù)雜,題目也就顯得更難了)

結(jié)構(gòu)體表示法

有的人是建立一個(gè)結(jié)構(gòu)體把集合表示出來喷户,如:

#define MAX 10000
struct Node
{
    int data;
    int rank;
    int parent;
 }node[MAX];

數(shù)組表示法

有的人則是弄很多相同大小的數(shù)組唾那,如:

int set[max];//集合index的類別,或者用parent表示
int rank[max];//集合index的層次褪尝,通常初始化為0
int data[max];//集合index的數(shù)據(jù)類型

//初始化集合
void Make_Set(int i)
{
    set[i]=i;//初始化的時(shí)候闹获,一個(gè)集合的parent都是這個(gè)集合自己的標(biāo)號(hào)期犬。沒有跟它同類的集合,那么這個(gè)集合的源頭只能是自己了避诽。
    rank[i]=0;
}
一般來說龟虎,題目簡(jiǎn)單用數(shù)組,題目復(fù)雜用結(jié)構(gòu)體沙庐,因?yàn)榻Y(jié)構(gòu)體有條理鲤妥,數(shù)組可以少打幾個(gè)字。

Find without PathCompression : 查看某個(gè)元素在不在集合中拱雏,返回parent代表元

就是找到parent指針的源頭棉安,可以把函數(shù)命名為get_parent(或者find_set,這個(gè)隨你喜歡铸抑,以便于理解為主)
如果集合的parent等于集合的編號(hào)(即還沒有被合并或者沒有同類)贡耽,那么自然返回自身編號(hào)。
如果不同(即經(jīng)過合并操作后指針指向了源頭(合并后選出的rank高的集合))那么就可以調(diào)用遞歸函數(shù)羡滑,如下面的代碼:

/**
*查找集合i(一個(gè)元素是一個(gè)集合)的源頭(遞歸實(shí)現(xiàn))菇爪。
 如果集合i的父親是自己,說明自己就是源頭柒昏,返回自己的標(biāo)號(hào)凳宙;
 否則查找集合i的父親的源頭。
**/
int get_parent(int x)
{
    if(node[x].parent==x)
        return x;
    return get_parent(node[x].parent);
}

數(shù)組的話就是:


//查找集合i(一個(gè)元素是一個(gè)集合)的源頭(遞歸實(shí)現(xiàn))
int Find_Set(int i)
{ 
    //如果集合i的父親是自己职祷,說明自己就是源頭氏涩,返回自己的標(biāo)號(hào)
   if(set[i]==i)
       return set[i];
    //否則查找集合i的父親的源頭
    return  Find_Set(set[i]);       
}

Union

合并過程.png

這就是所謂并查集的并了。至于怎么知道兩個(gè)集合是可以合并的有梆,那就是題目的條件了是尖。

先看代碼:

void Union(int a,int b)
{
    a=get_parent(a);
    b=get_parent(b);
    if(node[a].rank>node[b].rank)
        node[b].parent=a;
    else
    {   
        node[a].parent=b;
        if(node[a].rank==node[b].rank)
            node[b].rank++;
    }
}

再給出數(shù)組顯示的合并函數(shù):

void Union(int i,int j)
{
    i=Find_Set(i);
    j=Find_Set(j);
    if(i==j) return ;
    if(rank[i]>rank[j]) set[j]=i;
    else
    {
        if(rank[i]==rank[j]) rank[j]++;   
        set[i]=j;
    }
}

Union工作原理

  1. 每次做Union操作的時(shí)候都需要先做find操作,然后再合并兩個(gè)集合的祖先泥耀;

  2. K次union操作最多涉及2k個(gè)元素(假設(shè)每次做union操作的兩個(gè)元素術(shù)語(yǔ)不同的集合)

  3. 至少需要n-1次union才能把所有元素都合并成一個(gè)n元素的大集合饺汹。也就是說,每次union操作以后痰催,集合的數(shù)據(jù)最多減少一個(gè)(這里可以注意一下:因?yàn)橹蟮囊坏李}就會(huì)用到這個(gè)理論)

Union Optimal

Path Compression通過更改查找部分的代碼實(shí)現(xiàn)

image.png

parent[x] = find(parent[x]); //路徑壓縮

控制樹的高度來降低復(fù)雜度

一般來說兜辞,在union操作的時(shí)候,我們有兩個(gè)原則:

a. Link by size:節(jié)點(diǎn)較少的合并到節(jié)點(diǎn)多的夸溶;

b. Union by rank:高度較低的樹合并到高度較高的樹逸吵。

這兩個(gè)優(yōu)化的原理都是通過控制樹的高度來降低復(fù)雜度的,因?yàn)閒ind的時(shí)間復(fù)雜度取決于樹的高度缝裁。


image.png

Exercise

小試牛刀
Longest Consecutive Sequence
大展身手
Number of Islands
http://dwz.cn/39Y5PD

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扫皱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌韩脑,老刑警劉巖氢妈,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扰才,居然都是意外死亡允懂,警方通過查閱死者的電腦和手機(jī)厕怜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門衩匣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粥航,你說我怎么就攤上這事琅捏。” “怎么了递雀?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵柄延,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我缀程,道長(zhǎng)搜吧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任杨凑,我火速辦了婚禮滤奈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撩满。我一直安慰自己蜒程,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布伺帘。 她就那樣靜靜地躺著昭躺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伪嫁。 梳的紋絲不亂的頭發(fā)上领炫,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音张咳,去河邊找鬼帝洪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晶伦,可吹牛的內(nèi)容都是我干的碟狞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼婚陪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼族沃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤脆淹,失蹤者是張志新(化名)和其女友劉穎常空,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盖溺,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漓糙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烘嘱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昆禽。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蝇庭,靈堂內(nèi)的尸體忽然破棺而出醉鳖,到底是詐尸還是另有隱情,我是刑警寧澤哮内,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布盗棵,位于F島的核電站,受9級(jí)特大地震影響北发,放射性物質(zhì)發(fā)生泄漏纹因。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一琳拨、第九天 我趴在偏房一處隱蔽的房頂上張望瞭恰。 院中可真熱鬧,春花似錦从绘、人聲如沸寄疏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)陕截。三九已至,卻和暖如春批什,著一層夾襖步出監(jiān)牢的瞬間农曲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工驻债, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乳规,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓合呐,卻偏偏與公主長(zhǎng)得像暮的,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淌实,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容