不相交集是解决等价问题的一种有效的数据结构。
一、等价关系
等价关系定义如下:
1) 自反性:对于所有的a属于集合S, a与a有关系;
2) 对称性:如果a与b 有关系, 则b与a 有关系;
3) 传递性:如果a有b 有关系, b与c有关系, 则a 与 c 有关系;
在不想交集类中, 所有具有等价关系的类都在一个集合中,称为等价类; 不同的集合之间不存在等价关系;
为确定a 和 b是否具有关系, 我们只需要确定a 和 b 是否属于同一个等价类中;
一开始, 输入数据是N个数据, 每一个数据就是一个等价类(每个等价类中只有iyge元素);因此,初始时候,每个元素都是不相交的;
对不相交集允许两种操作:
1) find 操作
find 函数返回包含给定元素 的等价类的名字;
2) union 操作
如果要在a 和 b 之间建立等价关系, 首先需要确定a 和 b 是否已经具有等价关系了, 这可以通过对a 和 b执行find操作并检验他们是否在同一个等价类中完成。如果a 和 b 之间没有等价关系, 就将a 和 b 的两个等价类合并成一个新的等价类。
注意, 我们不进行任何比较元素相关的值得操作,只需要知道他们的位置。因此, 可以假设所有的元素均已从0到N-1 顺序编号, 并且编号方法容易由某个散列方案确定;
find操作返回的等价类的名字是很随意的, 不同的情况下a元素的等价类的名字可能会不同, 确定a 和 b是否有等价关系, 只要的是find(a) == find(b)
不相交集的find的操作在最坏的情况下运行时间为常数(当集合中每一个元素都是一个等价类的时候);
union 操作的最坏运行时间是常数(两个最大等价类合并到时候);
上述两个操作不能同时以常数最坏运行时间运行;
在执行union的时候, 通常将较小的等价类的名字改成较大的等价类的名字, 对于N-1 次合并到总的时间开销为O(NlogN)。 其原因在于, 每个元素可能将它的等价类最多改变logN 次;因此, 每次等价类改变时他的新的等价类至少是其原来等价类的两倍大小。使用这种方法, 任意顺序的M次find 和 N-1次union 最多花费 O(M + NlogN)的时间;
二、数据结构
使用树结构表示一个等价类, 树的集合是森林, 森林表示等价类的集合;
等价类的名字就是树的根;
使用一个数组, 数组的每个成员s[i] 表示元素i的父亲, 如果元素i是根, 那么s[i] = -1;
union 操作:
将一棵树的根雕父链 链接到另一棵树的根结点, 从而合并这两棵树。
find 操作:
对元素x的一次find(x) 操作通过返回这棵树的根完成;执行find的操作花费的时间与x结点深度成正比;x结点的深度可能是N-1,因此find操作的最坏情形的代价是O(N);
M次连续操作的最坏情形是O(MN);
相关代码如下:
class DisjSets {
public:
explicit DisjSets(int numElements);
int fin