2021.11.28——并查集


一位衩、關(guān)于并查集

1.1 并查集概念引入

并查集,顧名思義熔萧,就是合并查詢集合糖驴,是一種特殊的樹狀數(shù)據(jù)結(jié)構(gòu)。一般是用來解決元素分組的問題佛致,或是管理一些不相交的集合贮缕,他支持兩種操作:1.合并(Union)2.查詢(Find),比較典型的問題就是找親戚,最小生成樹的Kruskal算法等等,我們可以考慮使用并查集進(jìn)行維護(hù)俺榆。感昼。

并查集的主要思想是用一個(gè)集合中的元素來代表這個(gè)集合。

這么說可能有點(diǎn)抽象罐脊,有人用武俠的例子來生動(dòng)刻畫了這個(gè)并查集定嗓,有興趣可以了解一下 鏈接在這.

簡(jiǎn)單來說,就是有一堆不認(rèn)識(shí)的人萍桌,他們需要拉幫結(jié)派宵溅,這里需要解決兩個(gè)問題,第一上炎,怎么讓他們結(jié)盟(合并)层玲,第二,怎么知道他們是不是同一個(gè)幫派(查詢)

1.2 合并

根據(jù)這幾張圖可以感受一下他們是怎么樣建立關(guān)系的


并查集1.jpg

初始狀態(tài)下大家都是分散的反症,各自成一派辛块,所以自己的教主都是自己


并查集2.jpg

后面2和3想加入1的幫派,于是將他們的教主設(shè)為1铅碍,這樣我就只要知道他們教主是誰润绵,就知道他們是不是這幫派的人
并查集3.jpg

5 6和4同盟


并查集4.jpg

4又和1同盟了,那么大家全都是一家人胞谈,英文我們的教主都是1尘盼,咱們的頭是一樣的,那肯定是一幫子人烦绳。
并查集5.jpg

最后的樹結(jié)構(gòu)是這樣的

另外提一下卿捎,這里的教主比較專業(yè)的叫法是生成元。我們并查集就是通過生成元來判斷是不是隸屬于這個(gè)集合的

1.3 查詢

我們已經(jīng)知道了他們?cè)趺丛谕粋€(gè)集合里的径密,不就是看他們的教主嘛午阵,那我查他們是不是一路子人,就看他們教主是不是一樣的就可以了。

那現(xiàn)在的主要問題就是怎么知道他的教主底桂。

剛剛最后的圖樣很明顯植袍,是一個(gè)樹形的數(shù)據(jù)結(jié)構(gòu),我們通過訪問其上一級(jí)(引入pre數(shù)組訪問)籽懦,訪問到最上面的一層以后肯定能找到教主的于个。所以我們很自然想到的方法就是遞歸

直接上代碼暮顺。

二厅篓、代碼實(shí)現(xiàn)

初始化(每個(gè)人就是一個(gè)幫派,自己的教主就是自己本身)

void init(int num)
{
    //一開始是盤散沙捶码,每個(gè)人都是各立一派贷笛,
    //所以自立為王,他們的上一級(jí)(pre)就是他們自己
    for (int i = 1; i <= num; i++)
    {
        pre[i] = i;
    }
}

找生成元(遞歸思想)

int find(int key)
{
    if (pre[key] == key)
        return key;
    return find(pre[key]);
}

合并

void unite(int x, int y)
//并查集中的‘并’函數(shù)
//就是讓x,y結(jié)成同盟(讓x,y的老大都是一樣的)
//即隸屬于同一個(gè)集合內(nèi)
{
    int root_x = find(x);
    int root_y = find(y);
    if (root_x == root_y)
        return;
    else{
        pre[root_x] = root_y;
    }
}

三宙项、一些優(yōu)化

3.1 查詢路徑壓縮

我們希望我們只要找到最后的教主就好了乏苦,但是每一輪我都這樣遞歸訪問,明顯會(huì)有點(diǎn)麻煩尤筐,具體如圖汇荐。


并查集6.jpg

那有沒有解決問題的辦法呢?答案是有的盆繁,要解決遞歸引起的問題掀淘,我們就在遞歸上下點(diǎn)功夫,我們是否能將最后遞歸找到的教主油昂,賦值給每一個(gè)之前遞歸過程中的元素呢革娄?那我們多一個(gè)賦值操作就行了,后續(xù)我訪問的時(shí)候,至少這些訪問過的節(jié)點(diǎn)可以一步到位直接訪問到教主冕碟,這就是那些大佬們總是說的路徑壓縮拦惋。

并查集7.jpg
int find(int key)
//并查集中的’查‘函數(shù)
//找教主,要是不是自己的話安寺,就遞歸往上找
//這邊是優(yōu)化了一下算法厕妖,在遞歸過程中又賦值一下
//只要查過了一次老大后面就只要調(diào)用一次就能找到老大(root)了
{
    if (pre[key] == key)
        return key;
    return pre[key] = find(pre[key]);
}

厲害一點(diǎn)的可以直接一行三元運(yùn)算符?: 的代碼搞定

return x == fa[x] ? x : (fa[x] = find(fa[x]));

3.2 合并路徑壓縮(按秩合并)

那樣優(yōu)化以后,大家普遍會(huì)有一個(gè)認(rèn)識(shí)誤區(qū)挑庶,并查集最終生成的樹深度是2言秸,但是要注意路徑壓縮只在查詢時(shí)進(jìn)行,也只壓縮一條路徑迎捺,所以并查集最終的結(jié)構(gòu)仍然可能是比較復(fù)雜的

在合并問題上举畸,我們也可以留意一下,之前合并的操作的時(shí)候凳枝,或許你會(huì)想著一個(gè)問題抄沮,為什么要委屈x,讓他的幫主上一級(jí)變成y的幫主,讓y的幫主的上一級(jí)變成x的幫主不行嗎合是?其實(shí)是可以的,合理的變锭环,在路徑上甚至還會(huì)有所優(yōu)化聪全。這是大佬們說的并查集按秩合并。

并查集8.jpg

觀察這個(gè)圖可以發(fā)現(xiàn)辅辩,樹的深度變小了难礼,這意味著訪問教主所要遞歸的次數(shù)變少了。具體我們應(yīng)該把深度小的樹合并到深度大的樹上去玫锋,這樣合并以后蛾茉,到根節(jié)點(diǎn)距離變長的節(jié)點(diǎn)個(gè)數(shù)會(huì)更少。

代碼上需要做的改變是

這里我們引入一個(gè)Rank數(shù)組撩鹿,記錄每一個(gè)節(jié)點(diǎn)對(duì)應(yīng)的深度
初始化的時(shí)候我們做一點(diǎn)改變谦炬,剛開始的深度是1

void init(int n)
{
    for (int i = 1; i <= n; ++i)
    {
        fa[i] = i;
        rank[i] = 1;
    }
}

合并操作代碼優(yōu)化

void unite(int x, int y)
{
    int root_x = find(x);
    int root_y = find(y);
    //已經(jīng)是同一個(gè)集合的話就不需要做了
    if (root_x == root_y)
        return;

    //小樹并大樹
    if (depth[root_x] > depth[root_y])

        pre[root_y] = root_x;
    else
    {
        if (depth[root_x] == depth[root_y])
            depth[y]++;
        pre[root_x] = root_y;
    }
}

這里只有一種情況是要改變樹的深度的,那就是當(dāng)兩棵樹的深度相同時(shí)节沦,深度要加1键思。小數(shù)并大樹的時(shí)候,深度還是大樹的深度甫贯。不會(huì)變的

請(qǐng)看吼鳞,這是兩個(gè)樹深度相同的情況


1

插入到另外一組樹后,深度會(huì)+1叫搁,因?yàn)椴迦氲臉渖厦骓斨徊鍢涞母?jié)點(diǎn)赔桌,所以深度多了一,倒過來插也是一樣的渴逻。


2

四疾党、題目試煉

2017年第八屆 藍(lán)橋杯C組 C/C++決賽 合根植物

題目描述

w星球的一個(gè)種植園,被分成 m * n 個(gè)小格子(東西方向m行惨奕,南北方向n列)仿贬。每個(gè)格子里種了一株合根植物。
  這種植物有個(gè)特點(diǎn)墓贿,它的根可能會(huì)沿著南北或東西方向伸展茧泪,從而與另一個(gè)格子的植物合成為一體。

如果我們告訴你哪些小格子間出現(xiàn)了連根現(xiàn)象聋袋,你能說出這個(gè)園中一共有多少株合根植物嗎队伟?
輸入格式
  第一行,兩個(gè)整數(shù)m幽勒,n嗜侮,用空格分開,表示格子的行數(shù)、列數(shù)(1<m,n<1000)锈颗。
  接下來一行顷霹,一個(gè)整數(shù)k,表示下面還有k行數(shù)據(jù)(0<k<100000)
  接下來k行击吱,第行兩個(gè)整數(shù)a淋淀,b,表示編號(hào)為a的小格子和編號(hào)為b的小格子合根了覆醇。

格子的編號(hào)一行一行朵纷,從上到下,從左到右編號(hào)永脓。
  比如:5 * 4 的小格子袍辞,編號(hào):
  1 2 3 4
  5 6 7 8
  9 10 11 12
  13 14 15 16
  17 18 19 20

樣例輸入

5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17

樣例輸出

5

其合根情況參考下圖:


image.png

我的AC代碼附上~

#include <stdio.h>

const int MAX_N = 1000005;
int pre[MAX_N], depth[MAX_N];
int m, n, x, y, T,sum;

void init(int num)
{
    //一開始是盤散沙,
    //每個(gè)人都是各立一派常摧,所以自立為王搅吁,
    //他們的上一級(jí)(pre)就是他們自己
    for (int i = 1; i <= num; i++)
    {
        pre[i] = i;
        depth[i] = 0;
    }
}

int find(int key)
//并查集中的’查‘函數(shù)
//找教主,要是不是自己的話落午,就遞歸往上找似芝,
//這邊是優(yōu)化了一下算法,在遞歸過程中又賦值一下板甘,
//只要查過了一次老大后面就只要調(diào)用一次就能找到老大(root)了
{
    if (pre[key] == key)
        return key;
    return pre[key] = find(pre[key]);
}

void unite(int x, int y)
//并查集中的‘并’函數(shù)
//就是讓x,y結(jié)成同盟(讓x,y的老大都是一樣的)
//即隸屬于同一個(gè)集合內(nèi)
{
    int root_x = find(x);
    int root_y = find(y);
    if (root_x == root_y)
        return;
    if (depth[root_x] > depth[root_y])
        pre[root_y] = root_x;
    else
    {
        if (depth[root_x] == depth[root_y])
            depth[y]++;
        pre[root_x] = root_y;
    }
}

int main()
{
    // freopen("1.in", "r", stdin);
    // freopen("2.out", "w", stdout);
    scanf("%d %d", &m, &n);
    scanf("%d", &T);
    init(m * n);
    while(T--){
        scanf("%d %d", &x, &y);
        unite(x, y);
    }
    for (int i = 1; i <= m * n; i++)
    {
        if (find(i) == i)
        {
            sum++;
        }
    }
    printf("%d\n", sum);
    return 0;
}

后續(xù)關(guān)注更新~~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末党瓮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盐类,更是在濱河造成了極大的恐慌寞奸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件在跳,死亡現(xiàn)場(chǎng)離奇詭異枪萄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)猫妙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門瓷翻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人割坠,你說我怎么就攤上這事齐帚。” “怎么了彼哼?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵对妄,是天一觀的道長。 經(jīng)常有香客問我敢朱,道長剪菱,這世上最難降的妖魔是什么摩瞎? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮孝常,結(jié)果婚禮上旗们,老公的妹妹穿的比我還像新娘。我一直安慰自己构灸,他們只是感情好上渴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冻押,像睡著了一般驰贷。 火紅的嫁衣襯著肌膚如雪盛嘿。 梳的紋絲不亂的頭發(fā)上洛巢,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音次兆,去河邊找鬼稿茉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛芥炭,可吹牛的內(nèi)容都是我干的漓库。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼园蝠,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼渺蒿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彪薛,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤茂装,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后善延,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體少态,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年易遣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彼妻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豆茫,死狀恐怖侨歉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揩魂,我是刑警寧澤为肮,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站肤京,受9級(jí)特大地震影響颊艳,放射性物質(zhì)發(fā)生泄漏茅特。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一棋枕、第九天 我趴在偏房一處隱蔽的房頂上張望白修。 院中可真熱鬧,春花似錦重斑、人聲如沸兵睛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祖很。三九已至,卻和暖如春漾脂,著一層夾襖步出監(jiān)牢的瞬間假颇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工骨稿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笨鸡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓坦冠,卻偏偏與公主長得像形耗,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辙浑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 算法題目 力扣 <連通網(wǎng)絡(luò)的操作次數(shù)>[https://leetcode-cn.com/problems/numb...
    灰氣球閱讀 2,447評(píng)論 0 4
  • 并查集被很多OIer認(rèn)為是最簡(jiǎn)潔而優(yōu)雅的數(shù)據(jù)結(jié)構(gòu)之一激涤,主要用于解決一些元素分組的問題。它管理一系列不相交的集合判呕,并...
    Pecco閱讀 360評(píng)論 0 0
  • 參考 零基礎(chǔ)徹底弄懂"并查集" 1. 舉例分析 1.1. 案例說明 快過年了倦踢,犯罪分子們也開始為年終獎(jiǎng)“奮斗”了,...
    GOGOYAO閱讀 1,364評(píng)論 0 2
  • 用兩張圖告訴你佛玄,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料硼一? 從這篇文章中你...
    hw1212閱讀 12,714評(píng)論 2 59
  • 博主按:因?yàn)榻坛趟緢D片使用的是 github 倉庫圖片,網(wǎng)速過慢的朋友請(qǐng)移步《并查集:集合合并與元素查找》原文地...
    心譚閱讀 464評(píng)論 0 0