背景介绍(background):
等价关系(equivalence relation):是满足下列三个性质的关系:
1)自反性:对所有的, ;(其中表示关系);
2)对称性: 当且仅当;
3)传递性:若且则;
举例: 不具有等价性;电气连通性具有等价性。
输入数据最初是个集合的类(collection),每个集合含有一个元素,初始的所有关系均为false(除自反性),每个元素都有一个不同的元素,从而,这使得这些元素不相交;
不相交集类(disjointSet):
不相交集类是将一些元素合并为不相交的各个集合,在同一个集合中的元素两两等价,不同集合中的元素不等价。此时,有两种操作是允许的:
find:返回给定元素的集合的名字;
union:如果想添加关系,首先看二者是否存在关系(这可通过find检测二者是否在同一个类中),如果二者不在同一类中则对二者做union操作,将二者合并为一个新的等价类;
该算法是动态的,因为在算法的执行过程中,集合可以通过union改变而改变。
动态等价性问题解决方案:
1)保证指令find能够以常数最坏情形运行时间运行;
2)保证指令union能够以常数最坏情况运行时间运行;
已经证明二者不能同时以常数最坏情况运行时间运行;
基本数据结构:
非显示表示结果:
灵巧求并算法:
1)按大小求并:对其进行简单的改进是借助任意的方法打破现在的随意性,使得总是较小的树成为较大的树的子树。为了实现按大小合并的方法,需要记住每棵树的大小,可以让每个根元素包含它的树的大小的负值。合并的时候首先检查树的大小,将较小的树成为较大树的子树,新的树的大小为两棵树大小的和。
2)按高度求并: 跟踪每棵树的高度而不是大小并执行合并使得浅的树成为深的树的子树。只有两棵深度相等的树求并的时候树的高度才增加(树的深度加1),为了实现按高度求并,需要记住每棵树的高度,可以让每个根元素包含它的树的高度的负值。只有合并的两棵树高度相等的时候才需要更新树的高度(根元素的值减去1)。
按大小求并和按高度求并的非显示表示(根节点的“更新”准则不同,其他节点性质一致):
不相交类实例:
//disjointSet.cpp
#include<iostream>
#include<vector>
#include<iomanip>
using namespace std;
class DisjointSet{
public:
//DisjointSet constructor
explicit DisjointSet(int n);
//find value operation
int find(int value) const;
//union root2 to root1
void unionSet(int root1, int root2);
//union root2 to root1 by size
void unionSetBySize(int root1, int root2);
//union root2 to root1 by height
void unionSetByHeight(int root1, int root2);
//print the result
void print();
//reallocate s
void init(){
for(int i = 0; i < s.size(); i++){
s[i] = -1;
}
};
private:
vector<int> s;
};
DisjointSet::DisjointSet(int n = 10):s(n){
for(int i = 0; i < n; i++){
s[i] = -1;
}
}
int DisjointSet::find(int value) const{
if(s[value] == -1){
return value;
}else{
return find(s[value]);
}
}
void DisjointSet::unionSet(int root1, int root2){
s[root2] = root1;
}
void DisjointSet::unionSetBySize(int root1, int root2){
if(s[root2] < s[root1]){ //size root2 is larger
s[root2] += s[root1];
s[root1] = root2;
}else{ //size root1 is larger
s[root1] += s[root2];
s[root2] = root1;
}
}
void DisjointSet::unionSetByHeight(int root1, int root2){
if(s[root2] < s[root1]){ //root2 is deeper
s[root1] = root2;
}else if(s[root2] == s[root1]){ //the height is same
s[root1]--;
s[root2] = root1;
}else{ //root1 is deeper
s[root2] = root1;
}
}
void DisjointSet::print(){
cout << " the disjointSet info as follow " << endl;
for(typename vector<int>::iterator itr = s.begin(); itr != s.end(); ++itr){
cout << setw(4) << *itr << " ";
}
cout << endl;
for(int i = 0; i < s.size(); i++){
cout << setw(4) << i << " ";
}
cout << endl;
}
int main(){
cout << "simple disjointSet : " << endl;
DisjointSet disjointSet(8);
disjointSet.unionSet(4, 5);
disjointSet.unionSet(6, 7);
disjointSet.unionSet(4, 6);
disjointSet.unionSet(3, 4);
disjointSet.print();
/*for(int i = 0; i < disjointSet.s.size(); i++){
cout << disjointSet.find(i) << " ";
}
cout << endl;*/
cout << "union disjointSet by size : " << endl;
disjointSet.init();
disjointSet.unionSetBySize(4, 5);
disjointSet.unionSetBySize(6, 7);
disjointSet.unionSetBySize(4, 6);
disjointSet.unionSetBySize(3, 4);
disjointSet.print();
/*for(int i = 0; i < disjointSet.s.size(); i++){
cout << disjointSet.find(i) << " ";
}
cout << endl;*/
cout << "union disjointSet by height : " << endl;
disjointSet.init();
disjointSet.unionSetByHeight(4, 5);
disjointSet.unionSetByHeight(6, 7);
disjointSet.unionSetByHeight(4, 6);
disjointSet.unionSetByHeight(3, 4);
disjointSet.print();
/*for(int i = 0; i < disjointSet.s.size(); i++){
cout << disjointSet.find(i) << " ";
}
cout << endl;*/
cout << "done . " << endl;
return 0;
}
运行结果:
simple disjointSet :
the disjointSet info as follow
-1 -1 -1 -1 3 4 4 6
0 1 2 3 4 5 6 7
union disjointSet by size :
the disjointSet info as follow
-1 -1 -1 4 -5 4 4 6
0 1 2 3 4 5 6 7
union disjointSet by height :
the disjointSet info as follow
-1 -1 -1 4 -3 4 4 6
0 1 2 3 4 5 6 7
done .
practice makes perfect !