数据结构 并查集 路径压缩

经常会遇到对于给定的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;                     
}

这种非递归方式更安全。

    原文作者:犯罪团伙问题
    原文地址: https://blog.csdn.net/qq_27508477/article/details/47153027
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞