這一篇我們看看經典又神奇的並查集,顧名思義就是並起來查,可用於處理一些不相交集合的秒殺。
一:場景
有時候我們會遇到這樣的場景,比如: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 的複雜度,那麼有沒有...
經典演算法題每日演練 第二十五題 塊狀鍊錶
在資料結構的世界裡,我們會認識各種各樣的資料結構,每一種資料結構都能解決相應領域的問題,每一種資料結構都像 是降龍十八掌中的某一掌,掌掌斃命。當然每個資料結構,有他的優點,必然就有它的缺點,那麼如何創造一種資料結構 來將某兩種資料結構進行揚長避短,那就非常完美了。這樣的資料結構也有很多,比如 雙端佇...