并查集的基本实现 和 size优化&路径压缩
#include <iostream>
#include <ctime>
#include <cassert>
#define FOR(n) for( int i = 0 ; i < n ; i ++ )
using namespace std;
//朴素的并查集
class UnionFind{
private:
int* id;
int count;
public:
UnionFind( int n ){
id = new int[n];
this->count = n;
for( int i = 0 ; i < n ; i ++ )
id[i] = i;
}
~UnionFind(){
delete[] id;
}
//查找p的组
int find( int p ){
assert( p >= 0 && p < count );
return id[p];
}
//查看p和q是否在一组
bool isConnected( int p , int q ){
return find(p) == find(q);
}
//把q和q并在一起
void unionElements( int p , int q ){
int qID = find(q);
int pID = find(p);
if( pID == qID )
return;
for( int i = 0 ; i < count ; i ++ ){
if( id[i] == qID )
id[i] = pID;
}
}
void println(){
FOR(count)
cout << id[i] << endl;
}
};
2.采用指向父节点的思想实现
#include <iostream>
#include <ctime>
#include <cassert>
#define FOR(n) for( int i = 0 ; i < n ; i ++ )
using namespace std;
// 指向父节点的并查集
class UnionFind2{
private:
int* parent;
int count;
public:
UnionFind2( int n ){
parent = new int[n];
this->count = n;
FOR(n)
parent[i] = i;
}
~UnionFind2(){
delete[] parent;
}
//找p节点的根
int find( int p ){
assert( p >= 0 && p < count );
//当parent[p] = p的时候说明自己指向自己 即找到了"传进来"的p的根
while( parent[p] != p )
p = parent[p];
return p;
}
bool isConnected( int p , int q ){
//只需看是否在一个根下
return find(p) == find(q);
}
void unionElements( int p , int q ){
int Rootp = find(p);
int Rootq = find(q);
if( Rootp == Rootq )
return;
//这句话可以互换 即让Rootp的根指向根Rootq 这样他们就连在一起了
parent[Rootp] = Rootq;
}
void println(){
FOR(count)
cout << i << " ";
cout << endl;
FOR(count)
cout << parent[i] << " ";
cout << endl;
}
};
3.size优化
#include <iostream>
#include <ctime>
#include <cassert>
#define FOR(n) for( int i = 0 ; i < n ; i ++ )
using namespace std;
/************************************************************************/
/* size优化 */
/************************************************************************/
class UnionFind3{
private:
int* parent;
int* sz;
int count;
public:
UnionFind3( int n ){
parent = new int[n];
sz = new int[n];
this->count = n;
FOR(n){
parent[i] = i;
sz[i] = 1;
}
}
~UnionFind3(){
delete[] sz;
delete[] parent;
}
//找p节点的根
int find( int p ){
assert( p >= 0 && p < count );
//当parent[p] = p的时候说明自己指向自己 即找到了"传进来"的p的根
while( parent[p] != p )
p = parent[p];
return p;
}
bool isConnected( int p , int q ){
//只需看是否在一个根下
return find(p) == find(q);
}
void unionElements( int p , int q ){
int Rootp = find(p);
int Rootq = find(q);
if( Rootp == Rootq )
return;
//这句话可以互换 即让Rootp的根指向根Rootq 这样他们就连在一起了
//parent[Rootp] = Rootq;
if( sz[Rootp] < sz[Rootq] ){
parent[Rootp] = Rootq;
sz[Rootq] += sz[Rootp];
}
else{
parent[Rootq] = Rootp;
sz[Rootp] += sz[Rootq];
}
}
void println(){
FOR(count)
cout << i << " ";
cout << endl;
/* FOR(count)
cout << parent[i] << " ";
cout << endl;*/
FOR(count){
cout << i << "->" << parent[i] << endl;
}
}
};
4、路径压缩
#include <iostream>
#include <ctime>
#include <cassert>
#define FOR(n) for( int i = 0 ; i < n ; i ++ )
using namespace std;
// rank优化
/************************************************************************/
/* 路径压缩优化 */
/************************************************************************/
class UnionFind4{
private:
int* parent;
int* rank;
int count;
public:
UnionFind4( int n ){
parent = new int[n];
rank = new int[n];
this->count = n;
FOR(n){
parent[i] = i;
rank[i] = 1;
}
}
~UnionFind4(){
delete[] rank;
delete[] parent;
}
//找p节点的根
int find( int p ){
assert( p >= 0 && p < count );
//当parent[p] = p的时候说明自己指向自己 即找到了"传进来"的p的根
//while( parent[p] != p ){
// parent[p] = parent[parent[p]];
// p = parent[p];
//}
//return p;
if( p != parent[p] )
parent[p] = find(parent[p]);
return parent[p];
}
bool isConnected( int p , int q ){
//只需看是否在一个根下
return find(p) == find(q);
}
void unionElements( int p , int q ){
int Rootp = find(p);
int Rootq = find(q);
if( Rootp == Rootq )
return;
//这句话可以互换 即让Rootp的根指向根Rootq 这样他们就连在一起了
//parent[Rootp] = Rootq;
if( rank[Rootp] < rank[Rootq] )
parent[Rootp] = Rootq;
else if( rank[Rootq] < rank[Rootp] )
parent[Rootq] = Rootp;
//rank[Rootq] == rank[Rootp]
else {
parent[Rootp] = Rootq;
rank[Rootq] += 1;
}
}
void println(){
FOR(count)
cout << i << " ";
cout << endl;
FOR(count)
cout << parent[i] << " ";
cout << endl;
}
};
主函数进行时间性能的测试
#include <iostream>
#include <ctime>
#include <cassert>
#include "UnionFind1.h"
#include "UnionFind2.h"
#include "UnionFind3.h"
#include "UnionFind4.h"
#define FOR(n) for( int i = 0 ; i < n ; i ++ )
using namespace std;
void test1( int n ){
UnionFind uf(n);
time_t start = clock();
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf.unionElements(a,b);
}
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf.isConnected(a,b);
}
time_t end = clock();
cout << "Times:" << double(end-start)/CLOCKS_PER_SEC << "s" <<endl;
// uf.println();
}
void test2( int n ){
UnionFind2 uf2(n);
time_t start = clock();
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf2.unionElements(a,b);
}
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf2.isConnected(a,b);
}
time_t end = clock();
cout << "Times:" << double(end-start)/CLOCKS_PER_SEC << "s" <<endl;
uf2.println();
}
void test3( int n ){
UnionFind3 uf3(n);
time_t start = clock();
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf3.unionElements(a,b);
}
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf3.isConnected(a,b);
}
time_t end = clock();
cout << "Times:" << double(end-start)/CLOCKS_PER_SEC << "s" <<endl;
uf3.println();
}
void test4( int n ){
UnionFind4 uf4(n);
time_t start = clock();
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf4.unionElements(a,b);
}
for( int i = 0 ; i < n ; i ++ ){
int a = rand()%n;
int b = rand()%n;
uf4.isConnected(a,b);
}
time_t end = clock();
cout << "Times:" << double(end-start)/CLOCKS_PER_SEC << "s" <<endl;
uf4.println();
}
int main()
{
//对每一种实现方式进行时间测试
int n = 10;
// test1(n);
test2(n);
// test3(n);
// test4(n);
}