#include <stdio.h> /** * 並查集適用於管理元素分組情況的數據結構,本質上是樹結構(不是二叉樹) * 可以高效的進行以下操作: * 1.判斷兩個元素是否屬於同一組(通過判斷兩個元素的根節點是否一樣即可) * 2.合併兩個元素所在的組(把其中一個元素的根節點的父節點指向另一個元素的根節點) * * 在該實現下,進行一次操作的時間複雜度爲O(a(N)) * 其中N是元素個數,a(N)是阿克曼函數的反函數(比logN還快) */ #define N 10 /** * 假設開始時10個節點都分別是獨立的一棵樹: * 例如par[0]=0 表示0號節點的父節點爲它自己,也就代表整棵樹的根節點就是0號節點。 * 如果par[0]=1 則表示0號節點的父節點是1號節點 * 因爲開始時10個節點都是獨立的一棵樹,所以各顆樹的高度rank設爲0 */ static int par[N] = {0,1,2,3,4,5,6,7,8,9}; static int rank[N] = {0,0,0,0,0,0,0,0,0,0}; //查找樹的根 int findRoot(int x){ if (par[x] == x) return x; /** * 每次查找都會順便將沿途的節點進行路徑壓縮, * 所謂路徑壓縮,是指把當前節點的父節點直接指向根節點 * 這樣的話,下一次再次查找該節點的根節點的時候,就可以更快了 */ return par[x] = findRoot(par[x]); } //合併x和y的所屬集合 void unite(int x, int y){ x = findRoot(x); y = findRoot(y); //如果發現是同一個集合,則直接返回 if (x == y) return; //高度較低的樹要向高度較高的合併,以保持合併後整棵樹的平衡 if (rank[x] < rank[y]) { par[x] = y; } else { par[y] = x; if (rank[x] == rank[y]) rank[x]++; } } //判斷兩個節點是否屬於同一集合(判斷是否屬於同一根節點即可) int same(int x, int y){ return findRoot(x) == findRoot(y); } int main() { printf("%d\n", findRoot(2)); unite(1,2); unite(2,3); unite(5,3); printf("%d\n", findRoot(5)); printf("is same? %d", same(5,1)); return 0; }
運行結果:
2
1
is same? 1