/**
*优化方法二:快速union,慢find
*方法一合并时,需要将p元素集合中所有元素都合并到q集合中,效率为O(n)
*现只需将p元素所属集合中根节点所在的元素和q合并即可,构成一棵树,O(1)
*每个元素均有自己的父节点,根节点才是最终元素所属的集合
*合并时,将两个元素的根节点合并
*/
public class UnionFind2 {
private int count;
private int[]parent;
public UnionFind2(int n) {
count=n;
parent=new int[n];
//初始化时,数组中的元素均指向自己,以自己为父节点
for(int i=0;i<count;i++) {
parent[i]=i;
}
}
//不断寻找根节点即元素p所属集合
int find(int p) {
//p不能越界
if(p<0||p>=count)
return -1;
//如果元素p不是自己的本身的父节点,则p存在根节点,根节点即为p所属的最终集合
while(p!=parent[p]) {
p=parent[p];
}
return p;
}
boolean isConnected(int p,int q) {
return find(p)==find(q);
}
//将两个元素并在一个集合
public void union(int p,int q) {
int pRoot=find(p);
int qRoot=find(q);
//若根节点相同即在一个集合里
if(pRoot==qRoot)
return;
//若不在一个集合里,即将两个根节点相连
//将p的根节点指向q的根节点;
parent[pRoot]=qRoot;
}
//测试数组
public void printParent() {
for(int a:parent)
System.out.print(a+" ");
System.out.println();
}
}
/**
*优化方法三【基于size的优化】
*优化:增加一个存储数组下标元素每个根节点的元素数目
*因为方二中,总是固定将p的根节点指向q,有可能导致成为线性链表,树的深度变大
*所以可以提前判断下根节点的数目,总是让数目小的指向数目大的,让树尽量矮
*/
public class UnionFind3 {
private int count;
private int[]parent;
//size[i]即表示以i为根的集合中元素的个数
private int[]size;
public UnionFind3(int n) {
count=n;
parent=new int[n];
size=new int[n];
for(int i=0;i<count;i++) {
parent[i]=i;
//初始时,每个集合中元素个数均为1
size[i]=1;
}
}
int find(int p) {
if(p<0||p>=count)
return -1;
while(p!=parent[p])
p=parent[p];
return p;
}
boolean isConnected(int p,int q) {
return find(p)==find(q);
}
public void union(int p,int q) {
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)
return;
if(size[pRoot]<size[qRoot]) {
parent[pRoot]=qRoot;
//要维护树的个数
size[qRoot]+=size[pRoot];
}else {
parent[qRoot]=pRoot;
size[pRoot]+=size[qRoot];
}
}
public void printParent() {
for(int a: parent)
System.out.print(a+" ");
System.out.println();
}
}
/**
*优化方法四【基于rank的优化】
*方法三中只考虑集合元素个数,仍然会存在树的深度问题,
*EG:一个树深度低但是个数多,另一棵树深高但个数少,
*由个数判断,最后会让树深指向树浅,导致深度不断增加
*并查集越深,就越接近线性,find函数就越接近O(n)
*优化为:合并时,谁的深度深,谁就是新的根。
*这样集合的深度最多是最大深度的集合的深度,而不会让深度增加
*/
public class UnionFind4 {
private int count;
private int[] parent;
//rank[i]即表示以i为根的集合中树的深度
private int[]rank;
public UnionFind4(int n) {
count=n;
parent=new int[n];
rank=new int[n];
for(int i=0;i<count;i++) {
parent[i]=i;
//初始化时,每个元素树的深度均为1
rank[i]=1;
}
}
int find(int p) {
while(p!=parent[p])
p=parent[p];
return p;
}
boolean isConnected(int p,int q) {
return find(p)==find(q);
}
public void union(int p,int q) {
int pRoot=find(p);
int qRoot=find(q);
if(pRoot==qRoot)
return;
if(rank[pRoot]<rank[qRoot]) {
parent[pRoot]=qRoot;
}else if(rank[pRoot]>rank[qRoot]) {
parent[qRoot]=pRoot;
}else {
//深度相等时随便合并不影响结果
parent[pRoot]=qRoot;
rank[qRoot]+=1;
}
}
//测试数组
public void printParent() {
for(int a:parent)
System.out.print(a+" ");
System.out.println();
}
}
本文详细介绍了并查集的三种优化方法:快速union、基于size的优化和基于rank的优化。快速union通过合并根节点实现高效操作;基于size的优化通过比较根节点的元素数目,避免形成线性链表;基于rank的优化通过比较树的深度,确保合并后树的高度得到控制,从而保持find操作的高效性。


被折叠的 条评论
为什么被折叠?



