并查集是一种将同类或者相互联系事物归并并赋予同一类别标签的数据结构,这在现实世界中应用非常普遍。
顾名思义,并查集包含两个重要操作Union和Find,Union将两个对象合并,Find是查找某一个对象属于的类别或组份。
因此我们需要如下数据结构:
1.由对象编号(0,1,2...)索引的组份数组componentID,开始每一个对象都属于以自己为唯一元素的组分中。
2.记录组份数count
3.为了让树尽量平衡些,需要一个数组来记录各个节点的深度,让深度小的节点挂接在深度较大的节点上。
那么显然,如果在合并的过程中就将两个对象的组份ID设为一致,查找的时候就相当容易;如果在合并的过程中只是将一个对象与另一个对象相连,则查找的时候需要确定某个对象的始祖对象才能确定其所属的组份ID。
public class UnionFind { /* * 并查集用一个ID来标识一组具有某种传递性特征(导致相同特征)的对象 */ private int[] componentID; private byte[] rank;//rank[i]节点i的深度 private int count; // number of components /* * 初始化:将每个对象看成独立的组份,并给出该组分的ID(一般是该对象的自己的索引) */ public UnionFind(int n) { if(n<0) throw new IllegalArgumentException(); this.count=n; this.componentID=new int[n]; this.rank=new byte[n]; for(int i=0;i<n;i++) { componentID[i]=i; rank[i]=0; } } //连接两个节点,根据等级的不同 public void union(int p,int q){ int rootP=this.find_recursive(p); int rootQ=this.find(q); if(rootP==rootQ) return; if(rank[rootP] < rank[rootQ]) componentID[rootP] = rootQ; else if (rank[rootP] > rank[rootQ]) componentID[rootQ] = rootP; else { componentID[rootQ] = rootP; rank[rootP]++; } count--; /* if(rank[rootP]<rank[rootQ]) { for(int i=0;i<this.componentID.length && componentID[i]==rootP;i++) componentID[i]=rootQ; } else if(rank[rootP]>rank[rootQ]) { for(int i=0;i<this.componentID.length && componentID[i]==rootQ;i++) componentID[i]=rootP; } else { for(int i=0;i<this.componentID.length && componentID[i]==rootP;i++) componentID[i]=rootQ; rank[rootP]++; } count--; */ } //在查找p节点所属组份ID过程中会将实际上同属于一个组份的不同ID修改成相同 //非递归形式 public int find(int p) { int n=this.componentID.length; if(p<0||p>=n) throw new IllegalArgumentException("index " + p + " is not between 0 and " + (n-1)); while(p!=this.componentID[p]) { this.componentID[p]=componentID[componentID[p]]; p=componentID[p]; } return p; /* * if(p==componentID[p]) * return p; * * componentID[p]=find(componentID[p]) */ } //查找的递归形式 public int find_recursive(int p) { if(p==componentID[p]) return p; componentID[p]=find(componentID[p]); return componentID[p]; } public boolean isConnected(int p,int q) { return this.find(p)==this.find(q); } int count() { return this.count; } public static void main(String[] args) { // TODO Auto-generated method stub UnionFind uf=new UnionFind(10); uf.union(0,5); uf.union(1,6); uf.union(2,1); uf.union(3,4); uf.union(4,9); uf.union(5,6); uf.union(7,2); uf.union(8,3); uf.union(9,4); //容易发现该并查集属于同一组分的对象的直接ID不一致 System.out.println(uf.count); for(int i=0;i<uf.componentID.length;i++) System.out.println("index "+i+" linked to "+uf.componentID[i]); for(int i=0;i<uf.rank.length;i++) System.out.println("index "+i+" rank is "+uf.rank[i]); for(int i=0;i<uf.componentID.length;i++) System.out.println(i+" belongs to "+uf.find(i)); } }
下面是测试代码的测试结果:
2
index 0 linked to 0
index 1 linked to 0
index 2 linked to 0
index 3 linked to 3
index 4 linked to 3
index 5 linked to 0
index 6 linked to 1
index 7 linked to 0
index 8 linked to 3
index 9 linked to 3
index 0 rank is 2
index 1 rank is 1
index 2 rank is 0
index 3 rank is 1
index 4 rank is 0
index 5 rank is 0
index 6 rank is 0
index 7 rank is 0
index 8 rank is 0
index 9 rank is 0
0 belongs to 0
1 belongs to 0
2 belongs to 0
3 belongs to 3
4 belongs to 3
5 belongs to 0
6 belongs to 0
7 belongs to 0
8 belongs to 3
9 belongs to 3