操作
- 合并(Union):把两个不相交的集合合并为一个集合;
- 查询(Find):查询两个元素是否在同一个集合中。
并查集
重要思想:用集合中的一个元素代表集合
快速查询 quick find
/**
* 并查集
* 快速查找
* id数组存放集合元素
*/
public class QuickFindTest {
private static boolean find(int[] id, int p, int q) {
return id[p] == id[q];
}
private static int[] union(int[] id, int p, int q) {
if (find(id, p ,q)) {
return id;
}
int idp = id[p];
for (int i = 0; i < id.length; i++) {
if (id[i] == idp) {
id[i] = id[q];
}
}
return id;
}
}
find操作时间复杂度为O(1),而union操作时间复杂度为O(n)
快速合并 quick union
quick find算法的union操作时间复杂度较高,因此改变id数组存放数据的含义,有以下quick union算法
/**
* 并查集
* 快速合并
* id数组存放父元素下标
*/
public class QuickUnionTest {
private static boolean find(int[] id, int p, int q) {
return root(id, p) == root(id, q);
}
private static int root(int[] id, int i) {
while (id[i] != i) {
i = id[i];
}
return i;
}
private static int[] union(int[] id, int p, int q) {
int i = root(id, p);
int j = root(id, q);
id[i] = j;
return id;
}
}
find操作时间复杂度为O(logn),而union操作时间复杂度为O(logn)
按秩合并的快速合并 rank quick union
quick union算法平均复杂度虽然有O(logn),但是极端情况下树会退化为链表,复杂度会退化为O(n),因此,为了减小树的深度,在union时,按树的秩合并
/**
* 并查集
* 快速合并
* id数组存放父元素下标
* rank数组存放以i为根节点的树高
*/
public class RankQuickUnionTest {
private static boolean find(int[] id, int p, int q) {
return root(id, p) == root(id, q);
}
private static int root(int[] id, int i) {
while (id[i] != i) {
i = id[i];
}
return i;
}
private static int[] union(int[] id, int[] rank, int p, int q) {
int i = root(id, p);
int j = root(id, q);
if (rank[i] >= rank[j]) {
id[j] = i;
} else {
id[i] = j;
}
//当树高一致时,这时树高要加1
if (rank[i] == rank[j] && i != j) {
rank[i] ++;
}
return id;
}
}
路径压缩
即便union操作时考虑到树高,按秩合并,树高仍然也可能有log(n),对于root操作,复杂度依然是log(n),因此需要压缩路径来进一步减小树高;
如何进行路径压缩:将父节点直接置为根节点;
路径压缩的时机:root操作时
/**
* 并查集
* 并查
* id数组存放根元素下标
* rank数组存放以i为根节点的树高
*
* 增加路径压缩功能
*/
public class UnionFindTest {
private static boolean find(int[] id, int p, int q) {
return root(id, p) == root(id, q);
}
private static int root(int[] id, int i) {
return id[i] == i ? i : (id[i] = root(id, id[i]));
}
private static int[] union(int[] id, int[] rank, int p, int q) {
int i = root(id, p);
int j = root(id, q);
if (rank[i] >= rank[j]) {
id[j] = i;
} else {
id[i] = j;
}
if (rank[i] == rank[j] && i != j) {
rank[i] ++;
}
return id;
}
}