并查集及其优化
Posted xylee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集及其优化相关的知识,希望对你有一定的参考价值。
并查集
概述
性质
- 一种树形结构
- 并查集算法不支持分割一个集合
元素
- 代表元
- 集合中的元素,用来代表这个集合
- 一个集合内的所有元素组织成以代表元为根的树形结构
- parent[x]
- 对于每一个元素,parent[x]指向x在树形结构上的父亲节点。如果x是根节点,则令parent[x] = x
操作
- MakeSet
- 初始化并查集
- 设置一个代表
-
function MakeSet(x) // 参数 => 选定的代表元 x.parent := x
- Find
- 确定元素属于哪一个子集,返回元素所属集合的代表元
- 方法 => 可以沿着parent[x]不断在树形结构中向上移动,直到到达根节点
- 判断两个元素是否属于同一集合,只要看他们的代表元是否相同即可
-
function Find(x) // 参数 => 带查找的元素 if x.parent == x // 到达根节点 return x else return Find(x.parent)
- Union
- 将两个子集合并成同一个集合
-
function Union(x, y) xRoot := Find(x) yRoot := Find(y) xRoot.parent := yRoot
并查集Uuion优化
- 并查集最基础的表示方法为数组表示
- 性能劣于链表法,因为创建的树可能会严重不平衡,某些树枝深度太高
- 平衡优化
- 按秩合并
-
总是将更小的树连接至更大的树上
-
秩 : 深度
-
影响运行时间的是树深度,更小的树添加到更深的树的根上不会增加秩除非他们同秩
-
单个元素秩定义为0,当两棵秩同为r的树联合时,他们的秩为r+1
-
最坏时间复杂度O(logN)
-
function MakeSet(x) // 初始化 x.parent := x x.rank := 0 // 并查集树结构每个节点包含秩信息
-
function Union(x, y) // 合并 xRoot := Find(x) yRoot := Find(y) if xRoot == yRoot return // x和y不在同一个集合,合并他们。 if xRoot.rank < yRoot.rank xRoot.parent := yRoot else if xRoot.rank > yRoot.rank yRoot.parent := xRoot else yRoot.parent := xRoot xRoot.rank := xRoot.rank + 1
-
- 路径压缩
- 执行"查找"时扁平化树结构的方法
- 路径上每个节点都可以直接连接到根上
- Find递归地经过树,改变每一个节点的引用到根节点,得到的树更加扁平,为以后直接或者间接引用节点的操作加速
-
function Find(x) if x.parent != x x.parent := Find(x.parent) // 将每个节点引用到根节点 return x.parent
- 渐进最优算法 : Fredman和Saks在1989年解释了O(N)的平均时间可以获得任何并查集
- 按秩合并
- 平衡优化
主要操作
- 上面的操作都是假设元素分别独属于一个独立的集合里
- 合并两个不相交集合
- 设置一个parent数组,表示x的双亲编号
- 合并两个不相交集合的办法就是找到其中一个集合的始祖,将另外一个集合的始祖的父亲指向它
-
void Union(int x, int y) { xRoot = Find(x); // 找到始祖 yRoot = Find(y); // 找到始祖 if (xRoot != yRoot) parent[xRoot] = yRoot; // x -> y }
- 判断两个元素是否属于同一个集合
- 寻找始祖,比较始祖是否相同
-
bool same(int x, int y) { return Find(x) == Find(y); // 比较始祖是否相同 }
并查集的优化
- 路径压缩
-
int getfather(int v) { if (parent[v] == v) // 根节点 return v; // 返回始祖 else { parent[v] = getfather(parent[v]); // 路径压缩【递归】 return parent[v]; // 返回并查集根节点 } }
-
- Rank合并
- 将元素所在深度最小的集合合并到元素所在深度最大的集合
-
void judge(int x, int y) { // 寻找始祖 xRoot = Find(x); yRoot = Find(y); if (rank[xRoot] > rank[yRoot]) parent[yRoot] = xRoot; else { parent[xRoot] = yRoot; // 以yRoot为始祖 if (rank[xRoot] == rank[yRoot]) ++rank[yRoot]; // 重要的是始祖的rank,只修改始祖的,其他的不用管 } }
- 初始化
memset(rank, 0, sizeof(int)*rank_size);
时空复杂度
- 时间复杂度
- 同时使用路径压缩、按秩(rank)合并优化的程序每个操作的平均时间复杂度为O(N)
- 空间复杂度
- O(N) N为元素数量
以上是关于并查集及其优化的主要内容,如果未能解决你的问题,请参考以下文章