最近回顧了一下最小生成樹,發現以前學的太淺了。。。沒有仔細分析其中的每一步,汗顏。。發現Kruskal算法可以用到並查集的東西,就把並查集重新溫習了一下。注:這里有部分內容來自網絡,有部分內容在算法導論(第二版21章)中也有。
並查集
並查集是一種樹形結構,又叫“不相交集合”,保持了一組不相交的動態集合,每個集合通過一個代表來識別,代表即集合中的某個成員,通常選擇根做這個代表。
三種主要操作:
Make_Set(x):
建立一個新的集合,其唯一成員就是x,因此這個集合的代表也是x,並查集要求各集合是不相交的,因此要求x沒有在其他集合中出現過。
Find_Set(x):
返回能代表x所在集合的節點,通常返回x所在集合的根節點。有遞歸和非遞歸兩種方法,下面會有講解。
Union(x, y):
將包含x,y的動態集合合並為一個新的集合。合並兩個集合的關鍵是找到兩個集合的根節點,如果兩個根節點相同則不用合並;如果不同,則需要合並。
並查集的優化:
Union(x, y)時按秩合並:
合並時,如果兩個集合的秩相同,任選一個根做為父節點,並增加其秩。
秩不同時,讓較小秩的集合指向較大秩的集合,這時秩的大小不變。
秩和集合的數目是不一樣的,秩表示節點高度的一個商界;集合的數目表示集合中節點的總數。
Find_Set(x)路徑壓縮:
在Find_Set(x)中,是查找路徑上的每個節點都直接指向根節點,這樣下次再找根節點的時間復雜度會變成o(1)
主要代碼:
//建立一個新的集合,每一個子節點就是一個數,本身就是他的根節點 void Make_Set(int x) { father[x] = x; R[x] = 0; } //通過遞歸向上查找根節點,回溯時改變當前節點的父節點,直接指向根節點。 int Find_Set(int x) { if(x != father[x]) father[x] = Find_set(father[x]); return father[x]; } //將根節點設置為-1的非遞歸方法 int Find_Set2(int x) { int y = x; while(y!= -1) y = father[y]; return y; } //兩個集合的合並算法 void Union(int x, int y) { int GrandX = Find_set(x); int GrandY = Find_set(y); if(GrandX == GrandY) return; if(R[GrandX] < R[GrandY]) father[GrandX] = GrandY; else { if(R[GrandX] == R[GrandY]) R[GrandX]++; father[GrandY] = GrandX; } }
本文學習借助了勇幸的博客,感謝,這里注明鏈接:http://www.ahathinking.com/