您的位置:首页 > 产品设计 > UI/UE

union-find下(加权quick-union)算法

2017-12-20 11:04 288 查看
声明:

主要代码和部分算法说明参考自算法(第四版),这里将代码列出,是想和大家交流一些学习心得。

1.前言

在上一篇文章中,提到了quick-union算法的一个需要改进的地方,就是在union方法中,树的归并是随机的。如果我们规定节点数小的树总是归并到节点数大的树中,这样就会很大程度上减小树的深度,从而提高查找效率。

2.思路

创建一个数组,该数组保存的是各个触点的树的节点数,数组以触点为索引。

3.代码

以下代码在quick-union的算法的基础上进行添加

3.1

在类中新增如下成员变量:

private int[] sz; //存储每个树(连通分量)的节点数,以触点为索引


3.2

在构造方法中添加如下代码:

sz = new int
;
for(int i=0;i<N;i++) sz[i] = 1; //初始时每个分量树(只含一个触点)的节点数为1


3.3

修改union方法中的代码,修改后变为:

//将p和q的根节点统一
public void union(int p,int q){
int i = find(p); //找到p触点的根
int j = find(q); //找到q触点的根
if(i == j) return; //两根相同结束函数
//树i的节点数若小于j,则并入树j中,树j节点数加i。反之,将j并入i中。
if(sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; }
else { id[j] = i; sz[i] += sz[j]; }
count--; //连通分量的数量减1
}


4.测试

原始触点图:



原始sz[]:1 1 1 1 1 1 1 1 1 1

p:4 q:3

步骤:

sz[4]=1 >= sz[3]=1

sz[4]=sz[4]+sz[3]=1+1=2

触点图:



sz[]:1 1 1 1 2 1 1 1 1 1

p:3 q:8

触点图:



sz[]:1 1 1 1 3 1 1 1 1 1

p:6 q:5

触点图:



sz[]:1 1 1 1 3 1 2 1 1 1

p:9 q:4

触点图:



sz[]:1 1 1 1 4 1 2 1 1 1

p:2 q:1

触点图:



sz[]:1 1 2 1 4 1 2 1 1 1

p:8 q:9

触点图:

无变化

sz[]:无变化

p:5 q:0

触点图:



sz[]:1 1 2 1 4 1 3 1 1 1

p:7 q:2

触点图:



sz[]:1 1 3 1 4 1 3 1 1 1

p:6 q:1

触点图:



sz[]:1 1 3 1 4 1 6 1 1 1

p:1 q:0

触点图:

无变化

sz[]:无变化

p:6 q:7

触点图:

无变化

sz[]:无变化

5.完整代码

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;

//p145 union-find(加权quick-union)算法
public class WeightedQuickUnionUF{
private int[] id; //存储分量的数组,以触点作为索引
private int[] sz; //存储每个树(连通分量)的节点数,以触点为索引
private int count; //分量数量

//构造方法
public WeightedQuickUnionUF(int N){
count = N; //开始时,有N个分量,每个触点都构成了只含它自己的分量
//初始化分量数组
id = new int
;
for(int i=0;i<N;i++) id[i] = i; //以自己为父节点
sz = new int ; for(int i=0;i<N;i++) sz[i] = 1; //初始时每个分量树(只含一个触点)的节点数为1
}

//将p和q的根节点统一
public void union(int p,int q){
int i = find(p); //找到p触点的根
int j = find(q); //找到q触点的根
if(i == j) return; //两根相同结束函数
//树i的节点数若小于j,则并入树j中,树j节点数加i。反之,将j并入i中。
if(sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; }
else { id[j] = i; sz[i] += sz[j]; }
count--; //连通分量的数量减1
}

//找出分量名称(即找到该触点的根)
public int find(int p){
while(p != id[p]) p = id[p];
return p;
}

//判断p和q是否已连接
public boolean connected(int p,int q){
return find(p) == find(q); //依据:判断二者标识量是否相等
}

//连通分量的数量
public int count(){
return count;
}

//测试
public static void main(String[] args) {
int N = StdIn.readInt(); //读取触点数
WeightedQuickUnionUF wqu = new WeightedQuickUnionUF(N);
while(!StdIn.isEmpty()){
int p = StdIn.readInt(); //读取前一个数
int q = StdIn.readInt(); //读取后一个数
if(wqu.connected(p, q)) continue; //如果已经连接,则继续向下读取
wqu.union(p, q); //连接p、q
StdOut.println(p + " " + q); //打印刚刚建立的连接
}
StdOut.println(wqu.count + " components"); //打印连通分量的数量
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: