【问题标题】:Balancing a Binary Tree (AVL)平衡二叉树 (AVL)
【发布时间】:2010-09-13 02:43:39
【问题描述】:

好的,这是 CS 人员在理论领域的另一篇文章。

在 90 年代,我在实施 BST 方面做得相当不错。唯一让我无法理解的是平衡二叉树 (AVL) 的算法的复杂性。

你们能帮我解决这个问题吗?

【问题讨论】:

  • 你想让树完美平衡吗?最常见的算法保证一棵树在某种程度上是平衡的。例如,红黑树保证最深叶节点的深度不超过最浅叶节点深度的两倍
  • 另外,您是在寻找一种算法,它采用一棵树并对其进行平衡,或者作为树操作的一部分进行平衡,如插入、删除等。
  • “完美”当然必须定义。然而,在二叉树的上下文中,唯一有意义的定义是具有对数高度的二叉树,不是吗?
  • 您是否要求人们投票支持他们最喜欢的平衡树算法?答案是否取决于应用程序(例如,splay 树在重复最近搜索的情况下表现良好,AVL 完美平衡,红黑给出最漂亮的解释图等)
  • 对不起,错字。我的意思是“红黑在插入时具有良好的最坏情况性能”

标签: algorithm computer-science binary-tree theory avl-tree


【解决方案1】:

我认为在这里发布节点平衡算法的完整代码并不好,因为它们变得相当大。但是,red-black trees 上的 Wikipedia 文章包含该算法的完整 C 实现,AVL trees 上的文章也有几个指向高质量实现的链接。

这两种实现对于大多数通用场景来说已经足够了。

【讨论】:

  • 我不接受你的回答,因为它没有解释,只有链接。我的目的是获得一个有帮助的描述性答案。还是谢谢!
  • 您的问题询问的是代码,而不是解释。无论如何,我并不打算接受我的答案(因为它不是真正的答案)......你的新问题要好得多!
【解决方案2】:

替罪羊树可能有最简单的平衡确定算法可以理解。如果任何插入导致新节点太深,它会通过查看重量平衡而不是高度平衡来找到要重新平衡的节点。删除时是否重新平衡的规则也很简单。它不在节点中存储任何神秘信息。证明它是正确的比较棘手,但你不需要它来理解算法......

但是,与 AVL 不同,它并非始终保持高度平衡。像红黑一样,它的不平衡是有界的,但与红黑不同的是,它可以通过参数进行调整,因此对于大多数实际目的,它看起来就像你需要的一样平衡。不过,我怀疑如果你把它调得太紧,在最坏的情况下插入,它最终会比 AVL 更差或更差。

对问题编辑的回应

我将提供我个人理解 AVL 树的途径。

首先,您必须了解什么是树旋转,因此请忽略您听说过 AVL 算法的所有其他内容并理解这一点。直截了当地了解哪个是右旋哪个是左旋,以及它们对树的作用,否则对精确方法的描述只会让你感到困惑。

接下来,要了解平衡 AVL 树的技巧是每个节点在其中记录其左右子树的高度差。 “高度平衡”的定义是,对于树中的每个节点,它都介于 -1 和 1 之间。

接下来,了解如果您添加或删除了一个节点,您可能会使树不平衡。但是您只能更改作为您添加或删除的节点的祖先的节点的平衡。所以,你要做的就是沿着树往回走,使用旋转来平衡你找到的任何不平衡节点,并更新它们的平衡分数,直到树再次平衡。

理解它的最后一部分是在一个体面的参考中查找用于重新平衡您找到的每个节点的特定旋转:这是它的“技术”,而不是高级概念。您只需要在修改 AVL 树代码或可能在数据结构考试期间记住细节。自从我上次在调试器中打开 AVL 树代码以来已经有好几年了 - 实现往往会达到它们工作的程度,然后继续工作。所以我真的不记得了。您可以使用一些扑克筹码在桌子上解决问题,但很难确定您是否真的掌握了所有情况(数量不多)。最好只是查一下。

然后就是将其全部转换为代码的业务。

我认为查看代码清单对于除最后一个以外的任何阶段都没有多大帮助,因此请忽略它们。即使在最好的情况下,代码写得很清楚,它看起来就像教科书上的过程描述,但没有图表。在更典型的情况下,它是 C 结构操作的混乱。所以,只要坚持书本。

【讨论】:

  • 我接受你的回答,因为这是我想要的问题:一个很好的描述应该做什么。
  • 很高兴你喜欢它 - Konrad 的维基百科链接也很有用,因为它们提供了我遗漏的详细信息。
【解决方案3】:

我最近一直在使用 AVL 树。

我能找到的关于如何平衡它们的最佳帮助是通过谷歌搜索。

我只是围绕这个伪代码编写了代码(如果您了解如何进行旋转,它很容易实现)。

IF tree is right heavy
{
  IF tree's right subtree is left heavy
  {
     Perform Double Left rotation 
  }
  ELSE
  {
     Perform Single Left rotation
  }
}
ELSE IF tree is left heavy
{
  IF tree's left subtree is right heavy
  {
     Perform Double Right rotation
  }
  ELSE
  {
     Perform Single Right rotation
  }
}

【讨论】:

  • 这部分 AVL 相当简单 - 有点棘手的是在旋转后更新平衡因子。
【解决方案4】:

我同意,一个完整的程序是不合适的。

虽然经典的 AVL 和 RB 树使用确定性方法,但我建议看看“Randomized binary search trees”,它保持平衡的成本更低,并且无论键的统计分布如何都能保证良好的平衡。

【讨论】:

    【解决方案5】:

    AVL 树效率低下,因为每次插入/删除可能需要进行多次旋转。

    红黑树可能是更好的选择,因为插入/删除更有效。这种结构保证到叶子的最长路径不超过最短路径的两倍。因此,虽然不如 AVL 树平衡,但可以避免最严重的不平衡情况。

    如果您的树将被多次读取,并且在创建后不会被更改,那么对于完全平衡的 AVL 树来说,额外的开销可能是值得的。此外,红黑树需要为每个节点提供一个额外的存储空间,从而赋予节点的“颜色”。

    【讨论】:

    • 就我个人而言,我从未找到真正的解释 RB 树——只是列出规则。似乎没有人理解RB的why。 OTOH,AVL 对我来说直观且易于理解;你应该只写你理解的代码。
    • “为什么”:RB 树是 2-3-4 树到二叉树的映射,其中红边连接分裂的 2-3-4 树节点和黑边的部分对应于原始 2-3-4 树中的边。
    • 一件事,关于“每次插入/删除可能多次旋转”。 AVL 插入只需单转或双转即可恢复平衡。但是是的,Delete 最多可能有 O(log N) 次旋转。
    • IME,红黑树不是很快。它们的主要优点是它们在操作时间上的可变性很小。就速度而言,trap 很好——但它们的可变性更高。 stromberg.dnsalias.org/~strombrg/…
    【解决方案6】:

    为了平衡 AVL 树,我刚刚写了一堆函数,我想在这里与大家分享。我想这个实现是完美的。当然欢迎提问/提问/批评:

    http://uploading.com/files/5883f1c7/AVL_Balance.cpp/

    作为 Stackoverflow 的新手,我尝试在此处发布我的代码,但遇到了格式错误的问题。所以,把文件上传到上面的链接。

    干杯。

    【讨论】:

      【解决方案7】:

      自平衡 avl 树 @http://code.google.com/p/self-balancing-avl-tree/ 的完整实现。它还实现了对数时间连接和拆分操作以及地图/多地图集合

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-14
        • 2018-08-30
        相关资源
        最近更新 更多