目錄html
更新、更全的《數據結構與算法》的更新網站,更有python、go、人工智能教學等着你:http://www.javashuo.com/article/p-zfinzipt-hh.htmljava
在上一節 集合及運算中,咱們對集合使用二叉樹表示,以下圖所示:python
爲了使用二叉樹,咱們在上一節中使用如下代碼,構造二叉樹:算法
/* c語言實現 */ typedef struct{ ElementType Data; int Parent; } SetType; int Find(SetType S[], ElementType X) { // 在數組S中查找值爲X的元素所屬的集合 // MaxSize是全局變量,爲數組S的最大長度 int i; for (i = 0; i < MaxSize && S[i].Data != X; i++); if (i >= MaxSize) return -1; // 未找到X,返回-1 for (; S[i].Parent >= 0; i = S[i].Parent); return i; // 找到X所屬集合,返回樹根結點在數組S中的下標 }
使用二叉樹構造集合,Find操做在差的狀況下時間複雜度可能爲\(O(n^2)\)數組
所以對於任何有限集合的(N個)元素均可以被一一映射爲整數 0~N-1。即對於集合 {2, 5, 4, 3} 和 {6, 0, 1} 咱們可使用以下圖所示的數組表示:網絡
對於上述的數組,咱們可使用以下代碼構造:數據結構
/* c語言實現 */ typedef int ElementType; // 默認元素能夠用非負整數表示 typedef int SetName; //默認用根結點的下標做爲集合名稱 typedef ElementType SetType[MaxSize]; SetName Find(SetType S, ElementType X) { // 默認集合元素所有初始化爲-1 for (; S[X] >= 0; X = S[X]); return X; } void Union(SetType S, SetName Root1, SetName Root2) { // 這裏默認Root1和Root2是不一樣集合的根節點 S[Root2] = Root1; }
根據輸入樣例,以此來判斷計算機之間有多少個組成,以下圖所示框架
上圖動態變化以下圖所示:測試
下圖爲五臺計算機之間造成全鏈接狀態,所以當作一個總體:優化
/* c語言實現 */ int main() { 初始化集合; do { 讀入一條指令; 處理指令; } while (沒結束); return 0; } int main() { SetType S; int n; char in; scanf("%d\n", &n); Initialization(S, n); do { scanf("%c", &in); switch (in) { case 'I': Input_connection(S); break; // Union(Find) case 'C': Check_connection(S); break; // Find case 'S': Check_network(S, n); break; // 數集合的根,判斷計算機網絡的組成個數 } } while (in != 'S'); return 0; }
/* c語言實現 */ void Input_connection(SetType S) { ElementType u, v; SetName Root1, Root2; scanf("%d %d\n", &u, &v); Root1 = Find(S, u-1); Root2 = Find(S, v-1); if (Root1 != Root2) Union(S, Root1, Root2); }
/* c語言實現 */ void Check_connection(SetType S) { ElementType u, v; scnaf("%d %d\n", &u, &v); Root1 = Find(S, u-1); Root2 = Find(S, v-1); if (Root1 == Root2) printf("yes\n"); else printf("no\n"); }
/* c語言實現 */ void Check_network(SetType S, int n) { int i, counter = 0; for (i = 0; i < n; i++){ if (S[i] < 0) counter++; } if (counter == 1) printf("The network is connected.\n"); else printf("There are %d components.\n", counter); }
/* c語言實現 */ typedef int ElementType; // 默認元素能夠用非負整數表示 typedef int SetName; //默認用根結點的下標做爲集合名稱 typedef ElementType SetType[MaxSize]; SetName Find(SetType S, ElementType X) { // 默認集合元素所有初始化爲-1 for (; S[X] >= 0; X = S[X]); return X; } void Union(SetType S, SetName Root1, SetName Root2) { // 這裏默認Root1和Root2是不一樣集合的根節點 S[Root2] = Root1; }
對於上述的代碼,若是咱們放入pta中測試,會發現測試點6運行超時,以下圖所示:
所以,咱們會考慮是否是由於出現了某種狀況,致使Root2爲根結點的樹過大了,所以咱們修改代碼爲:
/* c語言實現 */ typedef int ElementType; // 默認元素能夠用非負整數表示 typedef int SetName; //默認用根結點的下標做爲集合名稱 typedef ElementType SetType[MaxSize]; SetName Find(SetType S, ElementType X) { // 默認集合元素所有初始化爲-1 for (; S[X] >= 0; X = S[X]); return X; } void Union(SetType S, SetName Root1, SetName Root2) { // 這裏默認Root1和Root2是不一樣集合的根節點 // S[Root2] = Root1; S[Root1] = Root2; }
發現更換代碼後,測試點5卻運行超時了,爲了解決上述問題,咱們可使用下面將要講到了按秩歸併的思想修改代碼。
爲何須要按秩歸併呢?由於咱們使用pta測試程序,發現代碼老是超時,所以咱們能夠考慮是否出現這種狀況——咱們再不斷地往一顆樹上累加子樹,以下圖所示:
/* c語言實現 */ Union(Find(2), Find(1)); Union(Find(3), Find(1)); ……; Union(Find(n), Find(1));
從上圖能夠看出,此過程的時間複雜度爲:\(T(n) = O(n^2)\)
除了上述這種狀況,會致使樹的高度愈來愈高,若是咱們把高樹貼在矮樹上,那麼樹高也會快速增加,所以咱們應該考慮把矮樹貼在高數上。
對於上述問題的解決,咱們給出如下兩個解決方法,這兩種方法統稱爲按秩歸併。
爲了解決上述問題,咱們能夠把根結點從-1替代爲-樹高,代碼以下:
/* c語言實現 */ if ( Root2高度 > Root1高度 ) S[Root1] = Root2; else { if ( 二者等高 ) 樹高++; S[Root2] = Root1; } if ( S[Root2] < S[Root1] ) S[Root1] = Root2; else { if ( S[Root1]==S[Root2] ) S[Root1]--; S[Root2] = Root1; }
爲了解決上述問題,咱們也能夠把根結點從**-1替代爲-元素個數(把小樹貼到大樹上),代碼以下:
/* c語言實現 */ void Union( SetType S, SetName Root1, SetName Root2 ) { if ( S[Root2]<S[Root1] ){ S[Root2] += S[Root1]; S[Root1] = Root2; } else { S[Root1] += S[Root2]; S[Root2] = Root1; } }
對於上述代碼超時的問題,咱們也可使用路徑壓縮的方法優化代,即壓縮給定元素到集合根元素路徑中的全部元素,詳細狀況以下圖所示:
上圖代碼可表示爲:
/* c語言實現 */ SetName Find(SetType S, ElementType X) { // 找到集合的根 if (S[X] < 0) return X; else return S[X] = Find(S, S[X]); }
總之上述代碼幹了這三件事:
所以,路徑壓縮第一次執行的時間比較長,可是若是頻繁使用查找命令,第一次將路徑壓縮,大大減少樹的高度,後續查找速度將大大增長
因爲pta並無嚴格控制時間限制,使用java這種語言,不使用路徑壓縮,問題不大,我寫這個也只是爲了回顧算法,來放鬆放鬆,不是爲了折騰本身,所以。
給你一個眼神本身體會,給你一個網址親自體會https://www.icourse163.org/learn/ZJU-93001?tid=1206471203#/learn/content?type=detail&id=1211167097&sm=1,我是懶得研究下圖所示了。