我们先来看一个模型图

图片

主要作用是在一堆数据集合中找到两个触点是否被链接在一起,如果不在一起将这两个点链接在一起,并且最后得出这种链接关系一共有多少个。

每个点都被称为一个触点,,每一个联系称为边,等价类称为连通分量

api如下

图片

我们首先给出一个实现

package UF;

/**

*

* @author Heaven

* 此方法为了研究触点问题,并开发出一套行之有效的API

*这里我们把触点问题简化成int数据类型的问题;

*此方法缺点:每次加连接都需要遍历

*Tip:触点问题类似病毒式扩散,碰到即赋值,大型问题直接GG

*/

public class UF {

private int [] id;

private int count;//剩余分量的数量

public UF(int N) {

id = new int [N];//创建一个数组格式来存储触点

count = N;

for (int i = 0; i < N; i++) {

id[i] = i;

}

}

public int count() {//调查数字

return  count;

}

public boolean connected(int p,int q) {//检测连接

return (find(p)==find(q));

}

public int find(int p) { //查询所在标识符

return id[p];

}

public void union(int p,int q) {//两者之间添加连接,请先确定两者是否连接

int findP = find(p);

int findQ = find(q);

if (connected(p, q))

return;

for (int i = 0; i < id.length; i++) {

if (id[i]==findP) {

findQ = id[i];

count–;

}

}

}

public static void main(String[] args) {//常见用例

int N = 10;//设置输入

UF uf =new UF(N);

int count = uf.count;

int p = 3;

int q = 8;

if (!uf.connected(p, q)) {

uf.union(p, q);

System.out.println(p+” “+q);

}

}

}

每次去连接,都是讲q设置为p,意味着连接了,但是这样的连接方式,我们使用的是直接遍历,导致的效率并不高

于是,我们改进了一个版本

package UF;

/**

*

* @author Heaven

* 此方法我们进一步优化UF结构

* 采用树的方式,只需要将两个节点的根节点连接即可

*/

public class UF_02 {

private int [] id;

private int count;//剩余分量的数量

public UF_02(int N) {

id = new int [N];//创建一个数组格式来存储触点

count = N;

for (int i = 0; i < N; i++) {

id[i] = i;

}

}

public int count() {//调查数字

return  count;

}

public boolean connected(int p,int q) {//检测连接

return (find(p)==find(q));

}

/**

*

* 在此次我们增加额外的判断,便于进行寻找根节点

*/

public int find(int p) { //查询所在标识符

if (p != id[p]) {

p = id[p];

}

return p;

}

/**

*

* 在此次我们增加额外的优化,将两个根节点统一

*/

public void union(int p,int q) {//两者之间添加连接,请先确定两者是否连接

int pRoot = find(p);

int qRoot = find(q);

if (pRoot == qRoot) {

return ;

}//不然进行赋值

id[pRoot] = qRoot;

}

public static void main(String[] args) {//常见用例

int N = 10;//设置输入

UF_02 uf =new UF_02(N);

int count = uf.count;

int p = 3;

int q = 8;

if (!uf.connected(p, q)) {

uf.union(p, q);

System.out.println(p+” “+q);

}

}

}

在进行连接之前,我们先进行查找根节点,然后进行连接

但是这并不是极致,因为我们还需要加权,即永远保证,小的树结构连接大的树结构,能够大大改进算法效率.加权的最坏情况:为所有要被归并的树大小都相同,均含有2的平方n个节点,但即使是这样,我们合并之后树的高度也为 2的(n+1)次方

package UF;

/**

*

* @author Heaven

* 此方法我们利用了权重的方法,列出两个数组,一个记录树结构,一个记录树的大小

* 每当新的连接,我们就直接寻找两个触点的根节点,将较小的树根节点连接到大的树根节点里面

*/

public class UF_03 {

private int[] sz;//记录各树的数量级

private int[] id;//记录各树的索引

private int count;

public  void WeightedQuickUnionUF(int N) {//创建两个数组

count = N;

id = new int [N];

for (int i = 0; i < id.length; i++) {

id[i] = i;

}

sz = new int [N];

for (int i = 0; i < sz.length; i++) {

sz[i] = i;

}

}

public int size() {

return count;

}//返回数量

public boolean connected(int p,int q) {

return find(p) == find(q);

}

private int find(int p) {//注意,这一步我要返回的是根节点

while (p != id[p]) {

p = id[p];

}

return p;

}

public void union(int p,int q) {

int n = find(p);

int m = find(q);

if (n == m)

return;

if (sz[n]>sz[m]) {//如果大于,那么把m贴在n上

id[m] = n;

sz[n]+=sz[m];

}else {

id[n] = m;

sz[m]+=sz[n];

}

count–;

}

}

无限的将小树依靠在大树上,从而保证对应的效率问题

图片

最后两个算法的对比

图片

发表评论

邮箱地址不会被公开。 必填项已用*标注