介绍
已知两个元素,如何快速判断两个元素是否属于同一个集合?已知两个集合,如何将两个集合合并为同一个集合?并查集就是解决这两个问题的数据结构。
并查集可以非常快判断网络中节点间的连接状态。
并查集是特殊的树结构,它是又孩子指向父亲。
注意并查集只关注两个点的连接问题,而不关注两个点的路径问题(路径问题显然比只关注连接要更复杂)。
并查集也不考虑添加和删除元素,只考虑当下的元素进行并或者查的操作。对于一组数据,并查集主要支持两个动作:
- unionElements(p, q),即将 p 和 q 两个元素所属的集合合并;
- isConnected(p, q),判断 p 和 q 两个元素是否属于同一个集合。
接口
并查集接口
1 2 3 4 5 6
| public interface UF {
int getSize(); boolean isConnected(int p, int q); void unionElements(int p, int q); }
|
Quick Find
这种实现方式只是在查询上比较快速,在合并操作上并不高效,并不是并查集的最终实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
public class UnionFind implements UF {
private int[] id;
public UnionFind(int size) { id = new int[size];
for (int i = 0; i < id.length; i++) { id[i] = i; } }
@Override public int getSize() { return id.length; }
private int find(int p) { if (p < 0 || p >= id.length) { throw new IllegalArgumentException("p is out of bound."); }
return id[p]; }
@Override public boolean isConnected(int p, int q) { return find(p) == find(q); }
@Override public void unionElements(int p, int q) { int pID = find(p); int qID = find(q);
if (pID == qID) { return; }
for (int i = 0; i < id.length; i++) { if (id[i] == pID) { id[i] = qID; } } } }
|
Quick Union
带路径压缩优化的 Quick Union 并查集实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
public class UnionFind implements UF {
private int[] parent; private int[] rank;
public UnionFind(int size) { parent = new int[size]; rank = new int[size];
for (int i = 0; i < size; i++) { parent[i] = i; rank[i] = 1; } }
@Override public int getSize() { return parent.length; }
private int find(int p) { if (p < 0 || p >= parent.length) { throw new IllegalArgumentException("p is out of bound."); }
while (p != parent[p]) { parent[p] = parent[parent[p]]; p = parent[p]; } return p; }
@Override public boolean isConnected(int p, int q) { return find(p) == find(q); }
@Override public 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] = qRoot; } else if (rank[pRoot] > rank[qRoot]) { parent[qRoot] = pRoot; } else { parent[qRoot] = pRoot; rank[pRoot] += 1; } } }
|