并查集是一种树型的数据结构,用于处理一些不相加集合的合并和查询问题。在使用中常常以森林来表示。 并查集也是用来维护集合的,和前面学习的set不同之处在于,并查集能很方便地同时维护很多集合。如果用set来维护会非常的麻烦。并查集的核心思想是记录每个结点的父亲结点是哪个结点。
// by Mastey_
并查集就是让这些人分为不同的派别,并且让每个帮派都有自己的老大,比如说张无忌,灭绝师太等等,这样可以通过查他们每个人都是哪一个派别就可以推断出他们在不在一个集合里,进而可以实现分类,合并以及查询等一系列操作。
并查集的一系列操作:
-
初始化:初始的时候每个结点各自为一个集合,father[i]表示结点 i 的父亲结点,如果 father[i]=i,我们认为这个结点是当前集合根结点。
-
查找:查找结点所在集合的根结点,结点 x 的根结点必然也是其父亲结点的根结点。
-
合并:将两个元素所在的集合合并在一起,通常来说,合并之前先判断两个元素是否属于同一集合。
路径压缩:
前面的并查集的复杂度实际上在有些极端情况会很慢。比如树的结构正好是一条链,那么最坏情况下,每次查询的复杂度达到了O(n) 。这并不是我们期望的结果。路径压缩的思想是,我们只关心每个结点的父结点,而并不太关心树的真正的结构。
这样我们在一次查询的时候,可以把查询路径上的所有结点的father[i]都赋值成为根结点。只需要在我们之前的查询函数上面进行很小的改动
int get(int x) {
if (father[x] == x) { // x 结点就是根结点
return x;
}
return father[x] = get(father[x]); // 返回父结点的根结点,并另当前结点父结点直接为根结点
}
路径压缩在实际应用中效率很高,其一次查询复杂度平摊下来可以认为是一个常数。并且在实际应用中,我们基本都用带路径压缩的并查集。