摘自:http://www.jisuanke.com/course/35/7069
并查集的查询操作最坏情况下的时间复杂度为O(n), 其中n为总元素个数。极端情况下,有根树会退化成一条链,而查询操作最坏情况下
会遍历整条链。由于合并操作需要用到两次查询操作,所以合并操作的最坏时间复杂度也为O(n);
为了解决出现这种极端情况,要进行更“智能”地合并:每次合并的时候都尽可能让合并后的有根树的最大深度不要过深,从而避免出现
类似一条链的情况发生。这就是并查集的按秩合并优化,一种启发式合并策略。在按秩合并中,秩是指结点高度的上界,或者说结点作为根
时所在的有根树的最大可能深度,具有较小秩的根在合并时要指向具有较大秩的根。因而,需要额外开辟一块空间来存储每个结点的秩,并
在并查集的合并操作红更新和使用它。
并查集增加了按秩合并优化以后,每次的操作的最坏时间复杂度从O(n)优化到O(log(n)),性能有了质的提升。
代码实现:
#include <iostream>
using namespace std;
class DisjointSet {
private:
int * father, * rank;//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) {
return find_set(father[node]);
}
return node;
}
bool merge(int node1, int node2) {
int ancestor1 = find_set(node1);
int ancestor2 = find_set(node2);
if (ancestor1 != ancestor2) {
//如果ancestor1的秩大于ancestor2的秩就交换两者
if (rank[ancestor1] > rank[ancestor2]) {
swap(ancestor1, ancestor2);
}
//让ancestor1指向ancestor2
father[ancestor1] = ancestor2;
//更新rank,用ancestor1的rank来更新ancestor2的rank
//更新后结果是ancestor1的rank加1和ancestor2的rank的较大值
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;
}