上一篇博客中,选择根节点数目少的节点指向根节点数目多的节点,这种策略一般是没有问题的,但是如果是下图的情况,要合并4和2
根据根节点数目少的指向根节点数目多的,4的根节点数目为3,2的根节点数目为6,那么需要将4的根节点8指向2的根节点7,但是这样树的高度变为4
而如果将2的根节点7指向4的根节点8,那么这样树的高度变为3,因此需要根据树的高度来判断谁指向谁
根据树的高度判断指向,称为基于rank的优化
程序实现为
#include <iostream>
#include <cassert>
#include "UnionFindTestHelper.h"
using namespace std;
int main()
{
int n = 1000000;
UnionFindTestHelper::testUF3(n);
UnionFindTestHelper::testUF4(n);
return 0;
}
"UnionFindTestHelper.h"定义为
//UnionFindTestHelper.h
#include <iostream>
#include <ctime>
#include <cstdlib> //rand()º¯Êý
#include "UnionFind1.h"
#include "UnionFind2.h"
#include "UnionFind3.h"
#include "UnionFind4.h"
using namespace std;
namespace UnionFindTestHelper{
void testUF3(int n){
srand(time(NULL));
UF3::UnionFind uf = UF3::UnionFind(n);
time_t startTime = 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 endTime = clock();
cout<<"UF3, "<<2*n<<" ops, "<<double(endTime-startTime)/CLOCKS_PER_SEC<<" s"<<endl;
}
void testUF4(int n){
srand(time(NULL));
UF4::UnionFind uf = UF4::UnionFind(n);
time_t startTime = 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 endTime = clock();
cout<<"UF4, "<<2*n<<" ops, "<<double(endTime-startTime)/CLOCKS_PER_SEC<<" s"<<endl;
}
}
本篇博客实现的基于rank的优化"UnionFind4.h"定义为
//UnionFind4.h
#include <iostream>
#include <cassert>
using namespace std;
namespace UF4 {
class UnionFind {
private:
int *parent;
int *rank;
int count;
public:
UnionFind(int n){
count = n;
parent = new int[count];
rank = new int[count];
for(int i = 0; i < count; i++){
parent[i] = i;
rank[i] = 1;
}
}
~UnionFind(){
delete[] parent;
delete[] rank;
}
int find( int p){
assert( p >= 0 && p < count );
while(p != parent[p])
p = parent[p];
return p;
}
bool isConnected(int p, int q){
return find(p) == find(q);
}
void unionElements(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if ( pRoot == qRoot )
return;
if (rank[pRoot] < rank[qRoot]){
parent[pRoot] = qRoot;
}
else if (rank[pRoot] > rank[qRoot]){
parent[qRoot] = pRoot;
}
else{ //rank[pRoot] == rank[qRoot]
parent[qRoot] = pRoot;
rank[pRoot] += 1;
}
}
};
}
"UnionFind3.h"定义见上篇博客
输出为
可以看出基于rank的优化并没有比上一个实现速度快,这是因为碰到需要rank的情况并不多见,而且甚至有时基于rank的优化还要比上一个实现要慢,但是基于rank的优化可以处理一些极端的情况。