經典演算法題每日演練 第十五題 並查集

2021-09-23 21:45:56 字數 3388 閱讀 7504

這一篇我們看看經典又神奇的並查集,顧名思義就是並起來查,可用於處理一些不相交集合的秒殺。

一:場景 

有時候我們會遇到這樣的場景,比如:m=,n=,我的需求就是判斷是否屬於同乙個集合,當然實現方法

有很多,一般情況下,普通青年會做出o(mn)的複雜度,那麼有沒有更輕量級的複雜度呢?嘿嘿,並查集就是用來解決這個問題的。

二:操作

從名字可以出來,並查集其實只有兩種操作,並(union)和查(find),並查集是一種演算法,所以我們要給它選擇乙個好的資料結構,

通常我們用樹來作為它的底層實現。

1.節點定義

#region 樹節點

/// /// 樹節點

///

public class node

#endregion

2.union操作

<1>原始方案

首先我們會對集合的所有元素進行打散,最後每個元素都是乙個獨根的樹,然後我們union其中某兩個元素,讓他們成為乙個集合,

最壞情況下我們進行m次的union時會存在這樣的乙個鍊錶的場景。

從圖中我們可以看到,union時出現了最壞的情況,而且這種情況還是比較容易出現的,最終導致在find的時候就相當寒酸苦逼了,為o(n)。

<2> 按秩合併

我們發現出現這種情況的原因在於我們union時都是將合併後的大樹作為小樹的孩子節點存在,那麼我們在union時能不能判斷一下,

將小樹作為大樹的孩子節點存在,最終也就降低了新樹的深度,比如圖中的union(d,)的時候可以做出如下修改。

可以看出,我們有效的降低了樹的深度,在n個元素的集合中,構建樹的深度不會超過logn層。m次操作的複雜度為o(mlogn),從代

碼上來說,我們用rank來統計樹的秩,可以理解為樹的高度,獨根樹時rank=0,當兩棵樹的rank相同時,可以隨意挑選合併,在新

根中的rank++就可以了。

#region 合併兩個不相交集合

/// /// 合併兩個不相交集合

///

///

///

///

public void union(char root1, char root2)

else

}#endregion

3.find操作

我們學演算法,都希望能把乙個問題優化到地球人都不能優化的地步,針對logn的級別,我們還能優化嗎?當然可以。

<1>路徑壓縮

在union和find這兩種操作中,顯然我們在union上面已經做到了極致,下面我們在find上面考慮一下,是不是可以在find上運用

伸展樹的思想,這種伸展思想就是壓縮路徑。

從圖中我們可以看出,當我find(f)的時候,找到「f」後,我們開始一直回溯,在回溯的過程中給,把該節點的父親指向根節點。最終

我們會形成乙個壓縮後的樹,當我們再次find(f)的時候,只要o(1)的時間就可以獲取,這裡有個注意的地方就是rank,當我們在路

徑壓縮時,最後樹的高度可能會降低,可能你會意識到原先的rank就需要修改了,所以我要說的就是,當路徑壓縮時,rank儲存的就

是樹高度的上界,而不僅僅是明確的樹高度,可以理解成"伸縮椅"伸時候的長度。

#region  查詢x所屬的集合

/// /// 查詢x所屬的集合

///

///

///

public char find(char x)

#endregion

我們注意到,在路徑壓縮後,我們將logn的複雜度降低到alpha(n),alpha(n)可以理解成乙個比hash函式還有小的常量,嘿嘿,這

就是演算法的魅力。

最後上一下總的執行**:

using system;

using system.collections.generic;

using system.linq;

using system.text; ;

disjointset set = new disjointset();

set.init(c);

set.union('e', 'f');

set.union('c', 'd');

set.union('c', 'e');

var b = set.issameset('c', 'e');

console.writeline("c,e是否在同乙個集合:", b);

b = set.issameset('a', 'c');

console.writeline("a,c是否在同乙個集合:", b);

console.read();}}

/// /// 並查集

///

public class disjointset

#endregion

dictionarydic = new dictionary();

#region 做單一集合的初始化操作

/// /// 做單一集合的初始化操作

///

public void init(char c));}

}#endregion

#region 判斷兩元素是否屬於同乙個集合

/// /// 判斷兩元素是否屬於同乙個集合

///

///

///

///

public bool issameset(char root1, char root2)

#endregion

#region 查詢x所屬的集合

/// /// 查詢x所屬的集合

///

///

///

public char find(char x)

#endregion

#region 合併兩個不相交集合

/// /// 合併兩個不相交集合

經典演算法題每日演練 第十五題 並查集

這一篇我們看看經典又神奇的並查集,顧名思義就是並起來查,可用於處理一些不相交集合的秒殺。一 場景 有時候我們會遇到這樣的場景,比如 m n 我的需求就是判斷是否屬於同乙個集合,當然實現方法 有很多,一般情況下,普通青年會做出o mn 的複雜度,那麼有沒有更輕量級的複雜度呢?嘿嘿,並查集就是用來解決這...

經典演算法題每日演練 第十五題 並查集

原文 經典演算法題每日演練 第十五題 並查集 這一篇我們看看經典又神奇的並查集,顧名思義就是並起來查,可用於處理一些不相交集合的秒殺。一 場景 有時候我們會遇到這樣的場景,比如 m n 我的需求就是判斷是否屬於同乙個集合,當然實現方法 有很多,一般情況下,普通青年會做出o mn 的複雜度,那麼有沒有...

經典演算法題每日演練 第二十五題 塊狀鍊錶

在資料結構的世界裡,我們會認識各種各樣的資料結構,每一種資料結構都能解決相應領域的問題,每一種資料結構都像 是降龍十八掌中的某一掌,掌掌斃命。當然每個資料結構,有他的優點,必然就有它的缺點,那麼如何創造一種資料結構 來將某兩種資料結構進行揚長避短,那就非常完美了。這樣的資料結構也有很多,比如 雙端佇...