Dynamic Connectivity
解决动态连接问题
Quick Find
Quick Union
N个对象
union操作:连接两个objects
find/connected操作:查询两个objects是否连接
connected components 联通分量:相互连接对象的最大集合
{0,1,2,5,6,7} {3,4,8,9}
Quick Find
有相同id的entry是连通的
union(p,q) 把第一项所在的component所有entry都改变为第二项id
public class QuickFind
{
private int[] id;
public QuickFind(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]; //忘记保留p来寻找p所在component
for(int i=0;i<id.length;i++){
if(id[i]==qid){
id[i]=qid;
}
}
}
}
Cost
Quick Union
数组存放父节点的id,有相同root的节点表示为连通
Find 检查是否具有相同的root
Union 把p的根节点变为q的根节点
public class QuickUnion{
private int[] id;
public QuickUnion(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 void union(int p,int q){
int rp=root(p);
int rq=root(q);
id[rp]=rq;
}
public boolean connected(int p,int q){
return root(p)==root(q);
}
}
Cost
当树很细长的时候,查询root节点为N次
QuickUnionImprovements
weighted Quick Union
设置一个权值避免产生过高的树,使得总是较小的树合并到较大树下面
设置一个数组记录每个树的size,合并p和q时,比较size,较小size的树合并到另一棵树下
public class QuickUnionImpro{
private int[] id;
private int[] sz;
public QuickUnionImpro(int N){
id=new int[N];
for (int i=0;i<N;i++) {
id[i]=i;
sz[i]=1;
}
}
private int root(int i){
while(i!=id[i])i=id[i];
return i;
}
public void union(int p,int q){
int rp=root(p);
int rq=root(q);
if(rp==rq) return;
if(sz[p]<=sz[q]){
id[p]=q;sz[q]+=sz[p];}
else{
id[q]=p;sz[p]+=sz[q];}
}
public boolean connected(int p,int q){
return root(p)==root(q);
}
}
任何数的depth小于等于以2为底的lgN
path compression
- 查询p节点的根节点后,把p的父节点设置为根节点
- 查询根节点时,直接指向祖父节点
private int root(int i){
while(i!=id[i])i=id[id[i]];
return i;
}
Cost
假设有N个objects,M次operations
最多进行N+M lg * N 次访问