标签:
并查集(Union-Find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题,在合并之前,需要先判断两个元素是否属于同一集合,这就需要用查找操作来实现。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。
并查集的实现原理也比较简单,就是使用树来表示集合,树的每个节点就表示集合中的一个元素,树根对应的元素就是该集合的代表,如下图所示。
图中有两棵树,分别对应两个集合,其中第一个集合为 {a,b,c,d},代表元素是 a;第二个集合为 {e,f,g},代表元素是 e。
树的节点表示集合中的元素,指针指向直接父节点,根节点的指针指向自己。沿着每个节点的指针不断向上查找,最终就可以找到该树的根节点,即该集合的代表元素。
假设使用一个足够长的数组来存储树节点,初始时要做的就是构造出下图的森林,其中每个元素都是一个单元素集合,即父节点是其自身:
并查集的合并非常简单,就是将一个集合的树根指向另一个集合的树根。这里可以应用一个简单的启发式策略——按秩合并。该方法使用秩来表示树高度的上界,在合并时,总是将具有较小秩的树根指向具有较大秩的树根。简单的说,就是总是将比较矮的树作为子树,添加到较高的树中。如图下所示。
对于并查集的查找操作,如果每次都沿着父节点向上查找,那时间复杂度就是树的高度,完全不可能达到常数级。这里需要应用另一种简单有效的启发式策略——路径压缩。在每次查找时,令查找路径上的每个节点都直接指向根节点,如下图所示。
题目来源:HDOJ-1232-畅通工程
首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性问题。比如随意给你两个点,让你判断它们是否连通,或者问你整幅图一共有几个连通分支,也就是被分成了几个互相独立的块。像畅通工程这题,问还需要修几条路,实质就是求有几个连通分支。如果是1个连通分支,说明整幅图上的点都连起来了,不用再修路了;如果是2个连通分支,则只要再修1条路,从两个分支中各选一个点,把它们连起来,那么所有的点都是连起来的了;如果是3个连通分支,则只要再修两条路……
源码如下:
#include<iostream> using namespace std; const int CMAX = 1001; // 最多1000个城镇(编号从1开始) int parent[CMAX]; // 存放各结点的直接父节点,对于根节点,parent指向它们本身 int Rank[CMAX]; // 存放各结点的秩,即该结点在子树中的高度(实际上只有根结点的秩在按秩合并时才有价值) int Find(int x) // 带有路径压缩的查找过程 { if (x != parent[x]) parent[x] = Find(parent[x]);// 递归阶段沿着查找路径向上直到找到根 return parent[x]; // 回溯阶段沿着搜索树向下更新每个结点,使其直接指向根 } void Union(int x, int y) // 按秩合并两个子树,让具有较小秩的根指向具有较大秩的根 { int root1 = Find(x); int root2 = Find(y); if (root1 != root2) // 根节点不同 { if (Rank[root1] < Rank[root2]) parent[root1] = root2; else { parent[root2] = root1; if (Rank[root1] == Rank[root2]) // 只有秩相等时需要递增根的秩(大树合并小树,根秩不变) Rank[root1]++; } } } int main() { int N, M; // 城镇数目和道路数目 while (scanf("%d%d", &N, &M) && N) // 当N为0时,输入结束 { int subtree_num = 0; // 子树个数,即连通分支数 for (int i = 1; i <= N; i++) // 初始化每个结点独自成树,秩为1 { parent[i] = i; Rank[i] = 1; } int city1, city2; for (int i = 1; i <= M; i++) // 读取M条道路,合并连通的城镇 { scanf("%d%d", &city1, &city2); Union(city1, city2); } for (int i = 1; i <= N; i++) { if (parent[i] == i) // 每找到一个根就有一个连通分支 subtree_num++; } printf("%d\n", subtree_num - 1); // 把这些连通分支连起来需要修subtree_num-1条路 } return 0; }
提交结果:
参考资料: 《算法导论第3版》—— 21.3 不相交集合森林
http://blog.csdn.net/dellaserss/article/details/7724401
http://www.cnblogs.com/cyjb/p/UnionFindSets.html
标签:
原文地址:http://www.cnblogs.com/eniac12/p/5505469.html