【问题标题】:Disjoint sets data structures and binomial trees?不相交集数据结构和二叉树?
【发布时间】:2012-12-12 21:56:53
【问题描述】:

有人可以解释什么是不相交集数据结构吗?或者将我链接到一个解释得很好的 YouTube 视频或文章。

几分钟前我搜索了它,我得到的只是一些数学课程,其中涉及一个看起来像维恩图的图像。也许就是这样,但我不确定,所以任何帮助表示赞赏。

顺便说一句,当我被问到“如何使用二叉树来表示二叉树队列中的每个二叉树”时,这是指您必须相互堆叠的二叉树吗?就像 B1 与 B1 连接成为 B2,然后两个 B2 成为 B3,依此类推。

【问题讨论】:

    标签: algorithm data-structures binary-tree disjoint-sets binomial-heap


    【解决方案1】:

    不相交集数据结构是表示partition of a set S 的数据结构。您从一组元素 S 开始,每个元素都属于自己的组。例如:

    {1} {2} {3} {4} {5} {6}
    

    对不相交集数据结构的一个操作是 union 操作,它将包含给定元素的两个集合组合在一起。例如,将 1 和 2 合并在一起会返回分区

    {1, 2} {3} {4} {5} {6}
    

    结合在一起 3 和 5 产生

    {1, 2}, {3, 5}, {4}, {6}
    

    现在,将 1 和 3 合并在一起会产生分区

    {1, 2, 3, 5}, {4}, {6}
    

    find 操作告诉您给定元素属于哪个集合。通常,这是通过 find 返回它所属元素的代表元素来完成的。通常这样做是为了

    find(x) == find(y)  if and only if  x and y are in the same set.
    

    例如,find(1) 可能返回 2,因此 find(2) = 2、find(3) = 2、find(5) = 2。

    不相交集数据结构通常用作 Kruskal 的最小生成树算法中的子程序,因为它们提供了一种非常快速的方法来检查图中的两个节点是否连接,以及一种简单的方法来标记两个连接组件中的所有节点当添加一条边时,它们相互连接。使用 disjoint-set forest 实现和联合排序和路径压缩,可以在 O(n α(n)) 时间内完成对不相交集森林的 n 次操作,其中 α(n)是inverse Ackermann function,一个增长如此缓慢的函数实际上是一个常数(对于小于宇宙大小的任何输入,它最多为四个。)


    关于二叉树和二叉树:我想您要问的是如何使用二叉树来表示二叉树,二叉树是多路树,最多有两个孩子。并非所有二叉树都是二叉树,因此必须使用合适的编码。

    一种方法是使用称为left-child right-sibling 表示的东西。根据以下设置,这将多路树表示为二叉树:

    • 每个节点的left子节点指向节点的第一个子节点。
    • 每个节点的子节点指向它的下一个兄弟节点(同层的节点和同一个父节点)。

    例如,给定这个二叉树:

         a
       / | \
      b  c  d
     /|  |
    e f  g
      |
      h
    

    左孩子右兄弟表示将是

                     a
                    /
                   b
                /    \
               e      c
                \    / \
                 f  g   d
                /
               h   
    

    顺便说一下 - 如果你在二叉树上这样做,你最终会得到一个二叉树的表示,称为 半序半树,它是一个二叉树,具有以下属性:

    • 树中的每个节点都大于或等于(或小于或等于,取决于这是最小堆还是最大堆)其左子树中的每个节点。
    • 根节点没有右子节点。

    这些定义源于二叉树是堆排序的,然后转换为左子右兄弟表示。使用这种表示,链接到二叉树的速度非常快。我将把它作为练习留给读者。 :-)

    希望这会有所帮助!

    【讨论】:

    • 非常感谢!我得到了大部分,现在它又回到了我的身边。不过,问题是,您能否向我解释或指出有关如何构建左孩子右兄弟姐妹表示的资源。我想我在那里错过了一步,我的大脑现在也有点炸了。谢谢!
    【解决方案2】:

    我在 uni 学到的不相交集围绕三个基本功能展开。

    make_set(x) - makes a new disjoint contains only the element x
    find_set(x) - gives you the set that contains element x
    union(x,y) - unions the sets that contain x and y
    

    他们提到的实现是使用链表。也就是说,每个集合都有一个代表创建该集合的元素。 (make_set(x)),然后用unions(x,y),将x的结束指针移动到指向yUnionmake_set 很快,但对于 find_set 来说这很慢(实际上是 O(最大集))

    更好的实现使用了两种称为路径压缩的方法,它作为一个元素通过union和/或find_set传递,使它指向集合的代表 p>

    另一个,union-by-rank,它为每个集合保持一个排名,给出集合的最大“深度”。联合时,如果每组的等级相同,则将等级增加一位,并更改一位代表指向另一位。如果它们不同,则将较小的集合更改为指向较大的代表,并且等级保持不变。 this 的渐近上界非常接近函数的使用次数。

    希望对您有所帮助。

    【讨论】:

      【解决方案3】:

      Disjoint set 基本上是一个联合查找数据结构。

      你原来有一组n节点,对它有find(node)union(node1,node2)操作。

      • union(node1,node2) 正在将节点“组合”在一组中
      • find(node) 正在寻找 node 的规范表示(通过 例如,给出根,后面会解释)

      例如,您最初拥有{1},{2},{3},{4},{5},而您却拥有:

      union(1,2)
      union(3,4)
      

      然后你最终拥有{1,2},{3,4},{5}
      这也意味着此时find(1) == find(2)(是同一套!)

      如果你稍后union(2,3) - 它将导致包含 2 的 set 与包含 3 的 set 合并,你最终会得到{1,2,3,4},{5} p>

      关于视频请求This lecture from Berkley 似乎很好地涵盖了材料。


      关于二叉树 - 它是一种实现方式,每个“根”都有其儿子,但树实际上是“颠倒的”,而不是从父亲到儿子的指针,而是从儿子到父亲的指针。
      这样,每个节点的规范表示是该节点通向的根,这确保了如果我们在ab 上进行联合,那么find(a) = find(b),因为它们具有相同的根.

      我希望它能给你一些关于这个 DS 是什么的线索。
      祝你好运!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-07
        • 1970-01-01
        • 1970-01-01
        • 2015-08-14
        相关资源
        最近更新 更多