经常会遇到对于给定的n个点,求出他们能否相互连通的问题,常规的做法可能是用二维数组进行标记处理,但是这样的空间使用是O(n^2),当数据量很大的时候,这个消耗是很恐怖的,所以这里我们引入并查集的这种数据结构。
树是一种数据结构,它的特点在于所有的点有且仅有一个根节点,如果我们能构造一棵树,并把所有的树按照相互关系装入这棵树,那么他们就是属于一个集合,他们都互相连通的。
我们可以用数组来构建这样一个模型。
int parent[N];
用这样一个数组,记录每个点属于哪个集合,最初所有的点都是孤立的。所以等于它本身。
每次我们都需要查询他们所属的集合,是否为同一个集合,如果不是,就可以把它们连通。
而且我们可以建立简单的树形关系:
这样查询的次数为层数。
注意有一种最坏的情况,就是如同链表那样,查询次数就接近总数的一半了。
int findx(int x)
{
int r=x;
while(parent[r] !=r)
r=parent[r];
return r;
}
这里我们给出一个很简单的查询方法,首先用变量r存储需要查询的值,然后每次查询r的父节点,保存它的父节点,查询它父节点的父节点……最后就能找到答案。
但我们想想,对于每个数,我们每次都需要去查询它根节点的时候都要很多时间,而且我们真实需要的并不是它的父节点的值,而是它根节点的值。
如果我们能构建如下的树:
这样查询的效率就是O(1),因为只有一层,所以我们查询的时间很少,这里就用到了路径压缩,我们先看压缩过的代码:
int findx(int x)
{
if (x != parent[x])
{
parent[x] = find(parent[x]);
}
return parent[x];
}
这样我们每次都会去更新父节点的值变成根节点的值。
但是用递归会出现一种问题,爆堆栈。
windows对堆栈的限制很大,所以我们可以放弃递归,用递推式。
int findx(int x)
{
int k, j, r;
r = x;
while(r != parent[r])
r = parent[r];
k = x;
while(k != r)
{
j = parent[k];
parent[k] = r;
k = j;
}
return r;
}
这种非递归方式更安全。