并查集
我们之前遇到的树结构都是由父亲指向孩子,但是并查集不一样,它是由孩子指向父亲的一种结构,并查集结构可以非常高效的回答连接问题(Connectivity Problem),它可以很快的判断网络中节点的连接状态。并查集主要支持两个动作
- union(p, q)
- 将元素p, q连接起来
- isConnected(p, q)
- 判断元素p, q是否是连接的,即是否所属一个集合
这里先给出并查集的接口,后面我们将实现多个版本的并查集
public interface UF {public int getSize();public boolean isConnected(int p, int q);public void unionElements(int p, int q);}
Quick Find

//第一版的并查集public class UnionFind1 implements UF{private int[] id;public UnionFind1(int size) {id = new int[size];//这时id全部为0,相当于在一个集合中 一开始应该全部不在一个集合中for (int i = 0; i < id.length; i++) {id[i] = i;}}@Overridepublic int getSize() {return id.length;}//找到元素p所属的集合private int find(int p) {if (p < 0 || p >= id.length) {throw new IllegalArgumentException("参数错误");}return id[p];}@Overridepublic boolean isConnected(int p, int q) {return find(p) == find(q);}@Overridepublic void unionElements(int p, int q) {if (find(p) == find(q)) {return;}for (int i = 0; i < id.length; i++) {if (id[i] == find(p)) {id[i] = find(q);}}}}
Quick Union

//第二版的并查集public class UnionFind2 implements UF{private int[] parent;public UnionFind2(int size) {parent = new int[size];for (int i = 0; i < parent.length; i++) {parent[i] = i;}}@Overridepublic int getSize() {return parent.length;}private int find(int index) {if (index < 0 || index >= parent.length) {throw new IllegalArgumentException("参数错误");}while (index != parent[index]) {index = parent[index];}return index;}@Overridepublic boolean isConnected(int p, int q) {return find(p) == find(q);}@Overridepublic void unionElements(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;} else {parent[pRoot] = parent[qRoot];}}}
基于rank的优化

//第三版的并查集public class UnionFind3 implements UF{private int[] parent;//记录根节点的高度private int[] rank;public UnionFind3(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < parent.length; i++) {parent[i] = i;rank[i] = 1;}}@Overridepublic int getSize() {return parent.length;}private int find(int index) {if (index < 0 || index >= parent.length) {throw new IllegalArgumentException("参数错误");}while (index != parent[index]) {index = parent[index];}return index;}@Overridepublic boolean isConnected(int p, int q) {return find(p) == find(q);}@Overridepublic void unionElements(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;}if (rank[pRoot] <= rank[qRoot]){parent[pRoot] = parent[qRoot];//只要在两个数的高度相等的时候 树的高度才会增加if (rank[pRoot] == rank[qRoot]) {rank[qRoot]++;}} else {parent[qRoot] = parent[pRoot];}}}
路径压缩

//第四版的并查集public class UnionFind4 implements UF{private int[] parent;private int[] rank;public UnionFind4(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < parent.length; i++) {parent[i] = i;rank[i] = 1;}}@Overridepublic int getSize() {return parent.length;}private int find(int index) {if (index < 0 || index >= parent.length) {throw new IllegalArgumentException("参数错误");}while (index != parent[index]) {//只添加了这一行代码parent[index] = parent[parent[index]];index = parent[index];}return index;}@Overridepublic boolean isConnected(int p, int q) {return find(p) == find(q);}@Overridepublic void unionElements(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;}//这时rank不代表高度 因为在路径压缩时没有维护rank//但是整体上rank还是能够表示大小关系的if (rank[pRoot] <= rank[qRoot]){parent[pRoot] = parent[qRoot];if (rank[pRoot] == rank[qRoot]) {rank[qRoot]++;}} else {parent[qRoot] = parent[pRoot];}}}

//第五版的并查集public class UnionFind5 implements UF{private int[] parent;private int[] rank;public UnionFind5(int size) {parent = new int[size];rank = new int[size];for (int i = 0; i < parent.length; i++) {parent[i] = i;rank[i] = 1;}}@Overridepublic int getSize() {return parent.length;}private int find(int index) {if (index < 0 || index >= parent.length) {throw new IllegalArgumentException("参数错误");}//修改了这里if (index != parent[index]) {parent[index] = find(parent[index]);}return parent[index];}@Overridepublic boolean isConnected(int p, int q) {return find(p) == find(q);}@Overridepublic void unionElements(int p, int q) {int pRoot = find(p);int qRoot = find(q);if (pRoot == qRoot) {return;}if (rank[pRoot] <= rank[qRoot]){parent[pRoot] = parent[qRoot];if (rank[pRoot] == rank[qRoot]) {rank[qRoot]++;}} else {parent[qRoot] = parent[pRoot];}}}
第五版的效率不一定比第四版的好,因为第四版最后也可能做到”扁平化”,并且第五版的递归操作比较耗时。
