动态连通性
给定一个含有N个元素的集合
- union/合并命令:连接两个对象
- connected/连通查询:是否有一条路径连接两个对象
连通栗子
Q:下图p和q是否连通?
A:是的
建模对象
可归结到连通性问题的现实问题
- 社交网络的朋友
- 计算机网络的节点
- 图片内的像素点
对每个对象以0-N-1进行编号
- 使用数字作为数组的索引
建模连通关系
对连通下定义:
- 反射性:p 连通 p
- 对称性:如果p连通q,那么q连通p
- 传递性:如果p连通q且q连通r,则p连通r
连通对象集合:集合内的对象相互连通
操作接口定义
查找查询:查看两个对象是否属于同一个连通对象集合
合并命令:将两个连通对象集合合并为一个连通对象集合
并查集API定义
目标:设计一个有效的数据结构
- 对象数目N将超大
- 对象操作M将超多
public class UF
UF(int N) //创建N个对象连通集合
void union(int p,int q) //对p,q做合并
boolean connected(int p,int q) //对p,q做查询
快速查找算法
快速查找
数据结构
- 一个长度为N的整形数组id[]
- 数组值:如果id[p] == id[q],那么认为p和q连通
查找:查看p,q是否有相同id
合并:将所有id == id[p]的替换为id[q]
JAVA实现
public class QFindUF
{
private int[] id;
public QFindUF(int N){
id = new int[N];
for(int i = 0;i<N;i++) id[i] = i;
}
public boolean connected(int p,int q) { return id[p] == id[q]; }
public void union(int p,int q){
int pid = id[p];
int qid = id[q];
for(int i = 0;i<id.length;i++) if(id[i] == pid) id[i] = qid;
}
}
快速查找太慢了
模型消耗:数组访问次数
合并操作消耗太大:对于N个对象的N次合并,需要消耗N^2的数组操作
快速合并算法
快速合并
数据结构
- 一个长度为N的整形数组
- 数组值:id[i]为i的根
- i的根为id[id[...id[i]...]]
查找:查看p和q是否有相同的根
合并:合并连通分量p和q,将p的根设置为q的根
JAVA实现
public class QUnionUF{
private int[] id;
public QUnionUF(int N){
id = new int[N];
for(int i = 0;i<N;i++) id[i]=i;
}
private int root(int i){
while(i != id[i]) i = id[i];
return i;
}
public boolean connected(int p,int q) { return root(p) == root(q); }
public void union(int p,int q){
int pr = root(p);
int qr = root(q);
id[pr] = qr;
}
}
快速合并也太慢了
模型消耗:数组访问次数
快速查找缺点:
- 合并操作消耗太大
- 树太扁平,需要花费很大的资源保持树的扁平
快速合并缺点:
- 树的深度太大
- 查找操作消耗太大
提升操作
提升手段一:加权重
带权重并查集:
- 避免树的深度太大
- 通过将小树挂靠到大树,来保持树的平衡因子
JAVA实现
数据结构:和快速合并一样,多出一个额外数组sz[i],统计以i为根的连通分量个数
查找:和快速合并一样
return root(p) == root(q);
合并:修改快速合并的合并操作为:
- 将小树挂靠到大树
- 更新sz数组
int pr = root(p);
int qr = root(q);
if(pr == qr) return;
if(sz[pr] < sz[qr]) { id[pr] = qr; sz[qr] += sz[pr]; }
else { id[qr] = pr; sz[pr] += sz[qr]; }
带权并查集分析
运行时间
- 查找:取决于p和q的深度
- 合并:常量时间
假设:对于每个节点x,认定深度为lgN
提升手段二:路径压缩
带路径压缩的快速合并:计算p的根时,将经过的节点的根直接指向总根
JAVA实现
只需要在循环内部增加一条语句
private int root(int i)
{
while(i != id[i]){
id[i] = id[id[i]];
i = id[i];
}
return i;
}
实际操作:不需要将树变得完全扁平
总结
- WQUPC将30年运行时间缩短到6秒
- 超级计算机帮助不会很大;good algorithm enables solution