yx_th000
cherish_yimi
(昨天和今天學習了並查集和
trie
樹,並練習了三道入門題目,理解更為深刻,覺得有必要總結一下,這其中的內容定義之類的是取自網路,操作的說明解釋及程式的注釋部分為個人理解。
並查集學習: l
並查集:(
union-find sets)
一種簡單的用途廣泛的集合. 並查集是若干個不相交集合,能夠實現較快的合併和判斷元素所在集合的操作,應用很多,如其求無向圖的連通分量個數等。最完美的應用當屬:實現kruskar演算法求最小生成樹。 l
並查集的精髓(即它的三種操作,結合實現**模板進行理解): 1
、make_set(x) 把每乙個元素初始化為乙個集合
初始化後每乙個元素的父親節點是它本身,每乙個元素的祖先節點也是它本身(也可以根據情況而變)。 2
、find_set(x) 查詢乙個元素所在的集合
查詢乙個元素所在的集合,其精髓是找到這個元素所在集合的祖先!這個才是並查集判斷和合併的最終依據。
判斷兩個元素是否屬於同一集合,只要看他們所在集合的祖先是否相同即可。
合併兩個集合,也是使乙個集合的祖先成為另乙個集合的祖先,具體見示意圖 3
、union(x,y) 合併x,y所在的兩個集合
合併兩個不相交集合操作很簡單:
利用find_set找到其中兩個集合的祖先,將乙個集合的祖先指向另乙個集合的祖先。如圖
l並查集的優化 1
、find_set(x)時 路徑壓縮
尋找祖先時我們一般採用遞迴查詢,但是當元素很多亦或是整棵樹變為一條鏈時,每次find_set(x)都是o(n)的複雜度,有沒有辦法減小這個複雜度呢?
答案是肯定的,這就是路徑壓縮,即當我們經過"遞推"找到祖先節點後,"回溯"的時候順便將它的子孫節點都直接指向祖先,這樣以後再次find_set(x)時複雜度就變成o(1)了,如下圖所示;可見,路徑壓縮方便了以後的查詢。 2
、union(x,y)時 按秩合併
即合併的時候將元素少的集合合併到元素多的集合中,這樣合併之後樹的高度會相對較小。
主要**實現
int father[max]; /* father[x]表示x的父節點*/
int rank[max]; /* rank[x]表示x的秩*/
/* 初始化集合*/
void make_set(int x)
/* 查詢x元素所在的集合,回溯時壓縮路徑*/
int find_set(int x)
return father[x];
}/*
按秩合併x,y所在的集合
下面的那個if else結構不是絕對的,具體根據情況變化
但是,宗旨是不變的即,按秩合併,實時更新秩。
*/void union(int x, int y)
else
father[x] = y;}}
注:學習並查集時非常感謝
slyar
另外,我認為寫並查集時涉及到的路徑壓縮,最好用遞迴,一方面**的可讀性非常好,另一方面,可以更直觀的理解路徑壓縮時在回溯時完成的巧妙。
並查集 並查集
本文參考了 挑戰程式設計競賽 和jennica的github題解 陣列版 int parent max n int rank max n void init int n int find int x else void union int x,int y else 結構體版 struct node ...
並查集入門(普通並查集 帶刪除並查集 關係並查集)
什麼是並查集?通俗易懂的並查集詳解 普通並查集 基礎並查集 例題 題解 how many tables problem description lh boy無聊的時候很喜歡數螞蟻,而且,還給每乙隻小螞蟻編號,通過他長期的觀察和記錄,發現編號為i的螞蟻會和編號為j的螞蟻在一起。現在問題來了,他現在只有...
並查集學習(2)
實現這個資料結構主要有三個函式 如下 void ufset 初始化 return i void union int r1,int r2 將兩個不同集合的元素進行合併,使兩個集合中任兩個元素都連通 else 在 函式中如果僅僅靠乙個迴圈來直接得到節點的所屬集合根結點的話。通過多次的 操作就會有很多節點...