zoukankan      html  css  js  c++  java
  • 重谈并查集

    重谈并查集

    当初学并查集的时候记得是森哥讲的@zcs0724。但是没太学明白(是因为蒟蒻太菜,跟森哥没半毛钱关系),于是今天来重新捋一下并查集,顺便补个讲解。就叫重谈并查集吧。(没有爆粗)(滕总笑)


    并查集的概念

    并查集(Disjoint-Set)是一种可以维护若干个不重叠的集合,并支持集合合并与查找的数据结构。

    (来自李煜东《算法竞赛进阶指南》)

    简单来讲,就是一个支持两个集合合并,支持查询一个元素在哪个集合里的数据结构。也就是有合并和查找两种操作。


    并查集的实现思路

    刚刚提到的并查集的基本概念是建立在“集合”这个定义上的。那么,我们肯定要选择一种方法来把集合表示出来。可以看出的是,如果对集合从1开始进行编号,那么最终的效率应该是很低的,所以我们选择用一个代表元素来表示这个元素所在的整个集合。

    那么,如果选择维护一个数组(f[i])表示(i)元素所在集合的编号(也就是代表元素),在每次合并的时候需要大量修改(f[i]),效率非常低。于是,我们继续思考,使用常见的数据结构:树来存储集合,令代表元素为树根。那么,一个并查集就是一个森林,一棵树就是一个集合。我们只需要维护(fa[i])表示这个节点的父亲,特别的,令树根的父亲为他自己。那么,在我们查询的时候,就可以一直递归查到根节点,即为答案。合并的时候,直接令一棵树的树根为另一棵树的树根的子节点即可。

    但是递归就很慢。

    怎么办呢?


    路径压缩

    刚刚提到过,我们的并查集已经优化到通过维护树来维护集合。但是在查询的时候,这样的最坏复杂度是(O(N))级别的。所以我们要考虑如何对其进行优化。

    思考优化其实就是在思考如何把最坏状况变成最好状况。很容易看出,这棵树的最好状况是只有两层,也就是所有节点要么是根节点,要么是它的儿子。

    灵光一闪:那么,如果我们在每次查询的时候,都把路径上经过的所有节点都直接指向根节点呢?是不是在之后的查询中就快很多了?复杂度是均摊(O(log N))的。


    启发式合并

    关于启发式合并,是多种数据结构合并时的一种思想,具体的蒟蒻有专门随笔讲解:

    浅谈启发式合并


    并查集的代码实现

    在这里给出带注释的路径压缩并查集的代码实现:

    int find(int x)
    {
        if(x==fa[x])
            return x;//如果查到根节点
        fa[x]=find(fa[x]);//过程中路径压缩
    }
    int find(int x)
    {
        return x==fa[x]?x:fa[x]=find(fa[x]);//以上代码的简化
    }
    void merge(int x,int y)//合并x,y元素所在的集合
    {
        int fx=find(x);
        int fy=find(y);//先查
        fa[fx]=fy;//直接合并
    }
    
  • 相关阅读:
    羅素悖論和正則公理
    「陶哲軒實分析」 習題 3.4.4
    常微分方程_阿诺尔德 1.1节,问题6 擴張相空間沿時間軸的平移變換將積分曲線變爲積分曲線
    「陶哲軒實分析」 習題 3.5.1
    常微分方程_阿诺尔德 1.1节,问题6 擴張相空間沿時間軸的平移變換將積分曲線變爲積分曲線
    「陶哲軒實分析」 習題 3.4.4
    css3的新属性 新增的颜色 透明度两种渐变定义多张背景图backgroundsize
    使用sessionStorage获取值和设置值
    PAT A1091 Acute Stroke
    杜教筛算法
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13710035.html
Copyright © 2011-2022 走看看