并查集:使用集合中某个元素来代表这个集合,这个集合组织成树状结构,所有元素指向根节点。
(1)维护一个数组,用于存储每个节点的父亲索引。
(2)主要涉及到三个操作:
(1)makeSet(int size):初始化集合,此时每个集合拥有一个元素,该元素指向自己。
(2)find(int x):找到x所在集合的代表元,常用来判断两个元素是否处于同一集合。
(3)union(int x,int y):合并x和y所在的集合,此时将一个根节点悬挂在另一个根节点下。具体的合并策略包括根据树的高度合并或者根据树包含的元素个数合并。
public void makeSet(int size){
for (int i=0;i<size ; i++)
unset[i] = i; //每个元素都是父亲
}
public int find(int x){
if(x!=unset[x])
unset[x]=find(unset[x]); //路径压缩,所有该条路线上的节点都指向根节点
return unset[x];
}
public void union (int x,int y){
if((x=find(x)==(y=find(y))) return; //x和y处于同一集合
if(rank[x]>rank[y]) { //维护一个rank数组,记录高度,每次加入高度低的树
unset[y]=x;
}else{
unset[x]=y;
if(rank[x]==rank[y]) rank[y]++; //更新树的高度
}
第二种设计:只需要维护一个unset数组,如果数组的值为正数,那么表示父亲节点的索引;如果数组的值为负数,那么表示该节点为根节点,负数的相反数表示树的元素个数。
public int find(int x){
if(unset[x]<0) return x;
unset[x]=find(unset[x]);
return unset[x];
}
public void makeSet(int size){
for(int i=0;i<size;i++)
unset[i]=-1;
}
public void union(int x,int y){
if((x=find(x))==(y=find(y))) return;
if(unset[x]<unset[y]){
unset[x]+=unset[y];
unset[y]=x;
}else{
unset[y]+=unset[x];
unset[x]=y;
}
}