如何快速地判断一个元素是否在一个集合中?如何将一个元素或一个集合合并到另一个集合中?并查集就是一种支持集合快速合并与查找的结构。简单的实现可以做到在O(1)时间复杂度内找到一个元素所属的集合,但是却要在O(N)时间内完成合并。假设有N个元素,编号为0到N-1,那么可以构造一个同样大小的整数数组A,A中每个元素存放对应集合元素所属类的编号,查找一个元素所在的集合时,到A中检查对应下标的元素就知道了一个所属的集合(O(1))。当要合并时,把涉及的元素在A中对应下标的元素设为合并后的集合标识即可(O(N))。复杂一点的实现则可以将合并与查找复杂度都做到O(lgN)。复杂做法将集合元素在数组A中组织成树状结构。与简单实现不同,数组A中不再简单地放置元素所属集合的标号,而是放置元素的父节点在原数组中下标,而根节点处放置集合元素个数的相反数。并查集的原理图如下:
如图,查找元素5所在集合时,先看位置5处的值,4>0,所以查看位置4处的值,为0,返回0.查找元素3所在的集合时,先看位置3处的值,-1 < 0,返回3,又查找元素5和元素3返回的值不同,所以元素5和元素3属于不同的集合。
下面看并查集完整代码:
#ifndef _UNIONFINDSET_H_
#define _UNIONFINDSET_H_
#include "Vector.h"
namespace MyDataStructure
{
class UnionFindSet
{
public:
UnionFindSet(){}
UnionFindSet(int capacity)
{
for (int i = 0; i < capacity;++i)
{
set.PushBack(-1);
}
}
UnionFindSet(const UnionFindSet& rhs)
{
set = rhs.set;
}
UnionFindSet& operator = (const UnionFindSet& rhs)
{
set = rhs.set;
return *this;
}
int Find(int index)
{
if (index < 0 || index >= set.Size()) return -1;
else if (set[index] < 0)
return index;
else
{
//路径压缩,但是由于合并时总是先找到集合的根,
//这一步的意义似乎不大了,所谓路径压缩是指
//在寻找某一元素根节点的路径上,将其所有的父节点
//都直接指向根节点,从而达到降低集合树高度的目的
return set[index] = Find(set[index]);
}
}
bool Union(int root1, int root2)
{
int s1 = Find(root1), s2 = Find(root2);
if (s1 >= 0 && s2 >= 0 && s1 != s2)
{
if (set[s1] <= set[s2])
{
set[s1] += set[s2];
set[s2] = s1;
}
else
{
set[s2] += set[s1];
set[s1] = s2;
}
return true;
}
return false;
}
private:
Vector<int> set;
};
}
#endif
测试代码:
// UnionFindSetTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "../include/UnionFindSet.h"
#include <iostream>
using namespace MyDataStructure;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
UnionFindSet ufs(21);
ufs.Union(0, 1);
ufs.Union(3, 1);
ufs.Union(2, 3);
ufs.Union(15, 19);
ufs.Union(15, 2);
cout << ufs.Find(0) << endl;
cout << ufs.Find(1) << endl;
cout << ufs.Find(2) << endl;
cout << ufs.Find(3) << endl;
cout << ufs.Find(91) << endl;
cout << ufs.Find(19) << endl;
cout << ufs.Find(15) << endl;
return 0;
}
程序运行结果一如所期,不再贴出。
并查集实现简单,速度快,用途广泛,Kruscal最小生成树算法中就用到并查集判断新加入的边是否会构成回路,后面的文章会看到。