摘自:http://www.jisuanke.com/course/35/7070
路径压缩优化是并查集最简单、也是非常有效的一个优化。
在执行find_set(a)的过程中,路径压缩优化会将从a到d路径上的所有点都指向有根树的根,也就是d。这么做的好处在于:通过这样的
处理,能将有根树的高度尽可能降低,下次再进行查询或合并操作的时候时间开销就更小了。路径压缩优化实现起来并不复杂,在find_set
函数中,当发现当前结点不是跟结点时,会不断的递归求解,并将结果直接作为返回值返回了。如果不直接返回,而是将当前结点的父结点
更新为递归函数的返回值呢?是不是递归的过程中将路径上的每个结点的父结点都设置成树的根了?
并查集通过同时增加按秩合并优化和路径压缩优化,最坏情况运行时间为O(a(n)), 其中a(n)是一个增长极其缓慢的函数,在读入时间可接受
的情况下,都有a(n) <= 4。因此,在各种实际情况中,可以把这个运行时间看作一个极小的常数。
代码实现:
#include <iostream>
using namespace std;
class DisjointSet {
private:
int *father, *rank;
public:
DisjointSet(int size) {
father = new int[size];
rank = new int[size];
for (int i = 0; i < size; ++i) {
father[i] = i;
rank[i] = 0;
}
}
~DisjointSet() {
delete[] father;
delete[] rank;
}
int find_set(int node) {
if (father[node] != node) {
//将递归调用的返回值赋给当前结点的father,即指向树根结点
father[node] = find_set(father[node]);
}
//返回树根结点
return father[node];
}
bool merge(int node1, int node2) {
int ancestor1 = find_set(node1);
int ancestor2 = find_set(node2);
if (ancestor1 != ancestor2) {
if (rank[ancestor1] > rank[ancestor2]) {
swap(ancestor1, ancestor2);
}
father[ancestor1] = ancestor2;
rank[ancestor2] = max(rank[ancestor1] + 1, rank[ancestor2]);
return true;
}
return false;
}
};
int main() {
DisjointSet dsu(100);
int m, x, y;
cin >> m;
for (int i = 0; i < m; ++i) {
cin >> x >> y;
bool ans = dsu.merge(x, y);
if (ans) {
cout << "success" << endl;
} else {
cout << "failed" << endl;
}
}
return 0;
}