实际使用场景:
求图的联通分量,比如求各城市之间互通还要修几条路,比如网络布线时最少还要布几条线防止多布线浪费材料。
UF.java
public class UF {
private int groupCount;//组的个数
private int[] pre;/*下标为节点在list中的索引,值为节点的前导节点的索引(父节点的索引)。
如果值小于0,那么表示没有父节点。并且其绝对值为树的节点个数。
*/
public UF(int size){
pre = new int[size];
groupCount = size;
for(int i=0;i<groupCount;i++){
pre[i] = -1;
}
}
public int[] getPre() {
return pre;
}
public void union(int p, int q){
int pg = findGroup(p);
int qg = findGroup(q);
if(pg!=qg){
//p所在树的节点数-q所在树的节点数 = -pre[pg] - (-pre[qg]) = pre[qg]-pre[pg]
if(pre[pg]<pre[qg]){//p所在树为大树
pre[pg] += pre[qg];//更新p所在树的节点数
pre[qg] = pg;
}else{
pre[qg] += pre[pg];
pre[pg] = qg;
}
groupCount--;
}
}
public int findGroup(int p){
if(pre[p]<0){//p没有父节点,那么它自己就是组长
return p;
}
int pg = p;
int p0 = p;
do{
/**优化方式一:找过的父节点,都缩短一次路径。方式二:目标节点,直接缩短至最短路径。*/
//pre[p] = pre[pg];
//p = pg;
pg = pre[pg];
}while(pre[pg]>-1);
pre[p0] = pg;
return pg;
}
/**
* 没有路径压缩的版本
* */
public int findGroupWithouOpti(int p){
if(pre[p]<0){//p没有父节点,那么它自己就是组长
return p;
}
int pg = p;
while(pre[pg]>-1){
pg = pre[pg];
}
return pg;
}
public boolean isConnected(int p, int q){
int pg = findGroup(p);
int qg = findGroup(q);
return pg == qg;
}
public int getGroupCount(){
return groupCount;
}
}
UFTest.java
public class UFTest {
@Test
public void testPerform2(){
int total = 1024*128;
Instant begin = Instant.now();
//分组规则:奇数偶数各为一组。
UF uf = new UF(total);
int size = 2;
while(uf.getGroupCount()>2){
size *= 2;
for(int i=0;i<total;i++){
if(i%size==0){
uf.union(i, i+size/2);
uf.union(i+1, i+size/2+1);
}
}
}
Instant end = Instant.now();
System.out.println(Duration.between(begin, end).toMillis());
System.out.println(uf.isConnected(total-1, 2));
System.out.println(uf.getGroupCount());
begin = Instant.now();
//路径压缩版:33ms,无路径压缩版:766ms
for(int i=0;i<total*100;i++){
uf.findGroup(1);
}
end = Instant.now();
System.out.println(Duration.between(begin, end).toMillis());
/*int[] pre = uf.getPre();
int pg = pre[1];
System.out.println(pg);
while(pg>0){
pg = pre[pg];
System.out.println(pg);
}*/
}
}
并查集
最新推荐文章于 2024-02-10 12:46:55 发布