#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