概述
上一篇实现的集合其实没什么卵用,跟个vector似的。并查集就比它厉害多了。
比如,有这么个场景。
有10台电脑(1,2,3…10),已知这些电脑之间已经实现了连接,1和2,2和4,3和5,4和7,5和8,6和9,6和10。
问:2和7之间是否连接?
上一篇实现的集合对解决这个问题没有任何帮助,然而用并查集就轻松多了,因为它能快速判断一个元素和另一个元素是否属于同一个集合,有多快?O(logn),如果使用了路径压缩优化的话,能达到渐进下的O(1),而且它只有两个操作,find和union,很简单。
实现
类声明:
template<typename T>
class DisjointSet {
public:
set(int maxsize);
virtual ~set();
public:
typedef struct {
T data;
int parent;
} SetType;
// insert a element as a unique set.
void insert(const T& data);
int find(const T& data);
int setUnion(const T& dataA, const T& dataB);
private:
vector<SetType> container_;
};
为什么要这么设计类?
- 初始化一个固定大小的数组来表示树,可以增加效率。
- 简洁,与并查集的特点相符。
所以,当要判断两个元素是否属于同一个集合,只要find(a)==find(b)
就行了,前提是这两个元素要先放到数组里面去。
来看一下find函数。思路就是先遍历一遍数组,看这个元素是否存在,然后去根据它的parent值往上寻找根结点,返回根节点的索引。所谓的路径压缩,就是把每个结点的父节点都直接指向它的根结点,从而减少下次查找的时间。
int find(const T& x) {
auto i = 0;
// traversal the container to get the index of x
for( ; i< container_.size() && container_[i].data_ != x ; i++) ;
// if didn't find return -1
if( i == container_.size()) return -1;
// get x 's root
auto ti = i; // for path compression
while( container_[i].parent_ > 0) {
i = container_[i].parent_;
}
// path compression
while( container_[ti].parent_ > 0) {
int before = ti;
ti = container_[ti].parent_;
container_[before].parent_ = i;
}
// find it, return index of root
return i;
}
来看一下union函数。思路就是先找到两个元素各自的根结点,然后将一方的根节点指向另一方的根节点就行了。这里还有个优化,就是按高度求并,也就是尽量让矮的树挂到高的树上,这样的好处是可以保证并完后的树的高度,只会在两棵树高度相等的时候增加1。
int setUnion( const T& a, const T& b) {
root1 = find(a);
root2 = find(b);
int ht1 = container_[root1].parent_, ht2 = container_[root2].parent_;
int newHight = ht1 + ht2;
if(ht1 > ht2)
container_[root2].parent_ = root1;
else
container_[root1].parent_ = root2;
}
应用
用它来做一下文章一开始提出来的那个计算机连接的问题。
DisjointSet<int> s(100);
for(auto i=1; i<11; i++)
s.insert(i);
s.setUnion(1, 2);
s.setUnion(2, 4);
s.setUnion(3, 5);
s.setUnion(4, 7);
s.setUnion(5, 8);
s.setUnion(6, 9);
s.setUnion(6, 10);
// then the sets would be {1,2,4,7} {5,3,8} {9,6,10}
assert( s.find(2) == s.find(7)); // so this is true
听说这个东西刷oj也很爽,来两道?
http://poj.org/problem?id=1611
http://poj.org/problem?id=2524