并查集,听起来可能有点陌生的数据结构,他和树的思想类似,但是肯定也有所不同。
今天我们就来简单介绍一下并查集的思想,和一步一步的优化。
首先简单的并查集是用数组实现的,如果要存储一些比较特殊的数据可以用类或者结构体来实现。
简单介绍,int 数组的实现,数组的下标表示这个数组的数,下标对应的数表示,这个数的父亲。
并查集的基本方法就是查找最顶层父节点,和两个数是否连接就是查找两个数的顶层节点是否相同。把两个数连接起来。
最简单的实现上代码:
public Integer find(int p) {
if(p<0||p>=arr.length) {
return null;
}
return arr[p];
}
public boolean isConnect(int p,int q) {
return find(p)==find(q);
}
public void connectElement(int p,int q) {
if(!(find(p)==find(q))) {
for(int i=0;i<arr.length;i++) {
if(arr[i]==find(q)) {
arr[i]=find(p);
}
}
}
}
但是这种效率比较低 尤其是在两个数连接的时候,效率尤其底下
我们只需要把要查找的两个数的顶层节点连接起来就行啦,不需要循环赋值代码如下
这样还能再次优化就是,在两个数的连接的时候,我们应该把当前级数最少的合并到多的那一列,就可以减少级数的增多。就需要声明一个级数数组,然后在connecElement的时候需要维护rank数组。
最后的优化就是,把所有的级数压缩到只有一层,父节点和子节点,这样保证级数最少,查找最快,连接最快,代码优化如下。
最后终极优化代码
package algorithm.unionfind;
/**
* @author 作者 liwei:
* @version 创建时间:2018年12月24日 下午1:16:12
* 类说明
*/
public class UnionFind2 {
private int [] arr;
private int [] sz;
private int [] rank;
private int [] parent;
public UnionFind2() {
super();
this.arr = new int [5];
this.rank = new int [20];
this.parent = new int [20];
}
public void init() {
for(int i=0;i<arr.length;i++) {
arr[i]=i;
rank[i]=1;
parent[i]=i;
}
}
public Integer find(int p) {
if(p<0||p>=arr.length) {
return null;
}
// while(p!=arr[p]) {
// arr[p]=arr[arr[p]];
// p=arr[p];
// }
if(p==arr[p])
return p;
int t=find(arr[p]);
arr[p]=t;
return arr[p];
}
public boolean isConnect(int p ,int q) {
return find(p)==find(q);
}
public void connectElementDev(int p,int q) {
Integer pRoot = find(p);
Integer qRoot = find(q);
if(pRoot==qRoot) return ;
if(rank[pRoot]>rank[qRoot]) {
arr[qRoot]=pRoot;
}
else if(rank[pRoot]<rank[qRoot]) {
arr[pRoot]=qRoot;
}
else {
arr[pRoot]=qRoot;
rank[qRoot] +=1;
}
}
public static void main(String[] args) {
UnionFind2 uf2 = new UnionFind2();
uf2.init();
int arr[]=uf2.arr;
for(int i =0;i<arr.length-1;i++) {
arr[i]=i+1;
}
arr[arr.length-1]=arr.length-1;
uf2.find(0);
}
}