并查集算法主要分为quic-find, quick-union以及union-find方法。但出于效率考虑,一般使用union-find算法进行实际操作。
union-find算法的思想就是利用一个数组id[]记录每个节点的父节点,例如现要将节点1(id[1] = 1)连接到节点2(id[2] = 2),则令id[1] = 2即可;同时,若节点为根节点,则令其id值等于其自身。通过这种方式,可将序列转化为由许多树组成的森林,若要判断两节点是否连通,只需比较二者根节点是否相同即可。
但为了进一步算法效率,我们还可以再创建一个数组sz[]来记录每棵树的大小,使得在将两棵树归并时只会将小树连接到大树上,从而尽可能减小最终结果中树的高度,进一步优化算法。
以下就是我用c++实现的union-find的代码:
#ifndef UNION_FIND_H
#define UNION_FIND_H
class union_find
{
private:
int id[]; //记录每个节点的父节点,根节点处为其自身
int sz[]; //记录节点处树的大小
int num; //记录总的联通分量数
public:
union_find(int N);
~union_find() {delete [] id; delete [] sz;};
int _num() {return num;};
bool connected(int p, int q) {return _find(p) == _find(q);}; //判断是否联通
int _find(int p); //寻找根节点
void _union(int p, int q); //连接两棵树
};
#endif
#include "union_find.h"
union_find::union_find(int N)
{
num = N;
for(int i; i < N; i++)
{
id[i] = i;
sz[i] = 1;
}
}
int union_find::_find(int p)
{
while(p != id[p]) //寻找p的根节点
p = id[p];
return p;
}
void union_find::_union(int p, int q)
{
int proot = _find(p);
int qroot = _find(q);
if(proot != qroot) //判断两节点是否是联通的
{
if(sz[proot] < sz[qroot]) //判断树的大小
{
id[proot] = qroot; //小树的根节点接到大树根节点上
sz[qroot] += sz[proot]; //大树的元素数量等于其自加小树的节点数
}
else
{
id[qroot] = proot;
sz[proot] += sz[qroot];
}
}
num--; //分量数减一
}