平衡二叉树与索引跳过列表

Posted

技术标签:

【中文标题】平衡二叉树与索引跳过列表【英文标题】:Balanced binary trees versus indexed skiplists 【发布时间】:2013-04-01 17:17:57 【问题描述】:

不确定问题应该在这里还是程序员(或其他一些 SE 站点),但我很好奇平衡二叉树和 indexable skiplists 之间的相关差异。这个问题是在this question 的上下文中出现的。来自***:

跳过列表是一种概率数据结构,似乎很可能取代平衡树,成为许多应用程序选择的实现方法。跳过列表算法具有与平衡树相同的渐近预期时间界限,并且更简单、更快且占用空间更少。

skiplist 的空间需求不是取决于层次结构的深度吗?并且二叉树不是更容易使用吗,至少对于搜索(在平衡 BST 中授予,插入和删除可能很棘手)?跳过列表还有其他优点/缺点吗?

【问题讨论】:

【参考方案1】:

(您的问题的某些部分(易用性、简单性等)有点主观,我将在本文末尾回答。)

让我们看看空间使用情况。首先,假设您有一个具有 n 个节点的二叉搜索树。所需的总空间使用量是多少?好吧,每个节点都存储一些数据和两个指针。您可能还需要一些信息来维护余额信息。这意味着总空间使用量为

n * (2 * sizeof(指针) + sizeof(数据) + sizeof(余额信息))

让我们考虑一个等效的跳过列表。您绝对正确,skiplist 使用的实际内存量取决于节点的高度,但我们可以讨论skiplist 使用的预期空间量。通常,您从 1 开始选择跳过列表中节点的高度,然后反复掷硬币,只要您翻转正面就增加高度,并在您翻转反面时立即停止。鉴于此设置,跳过列表中的预期指针数量是多少?

概率论的一个有趣结果是,如果您有一系列概率为 p 的独立事件,则在该事件发生之前您需要大约 1 / p 次试验(按预期)。在我们抛硬币的例子中,我们一直在抛硬币直到它出现反面,并且由于硬币是公平的硬币(正面朝上的概率为 50%),所以在我们反面之前所需的预期试验次数是 2。由于最后一次翻转结束了增长,因此一个节点在跳过列表中增长的预期次数为 1。因此,按照预期,我们预计一个平均节点中只有两个指针——一个初始指针和一个添加指针。这意味着预期的总空间使用量是

n * (2 * sizeof(指针) + sizeof(数据))

将此与平衡二叉搜索树中节点的大小进行比较。如果存储余额信息所需的空间量不为零,则跳过列表确实将使用(按预期)比平衡 BST 更少的内存。请注意,许多类型的平衡 BST(例如treaps)需要大量平衡信息,而其他类型(red/black trees、AVL trees)具有平衡信息,但可以将该信息隐藏在其指针的低位中,而其他人 (splay trees) 则根本没有任何余额信息。因此,这不是保证的胜利,但在许多情况下它会占用空间。

至于您关于简单性、易用性等的其他问题:这真的取决于。我个人发现在 BST 中查找元素的代码比在跳过列表中查找元素的代码要容易得多。然而,平衡 BST 中的旋转逻辑通常比跳过列表中的插入/删除逻辑复杂得多;试着看看你是否可以在不参考参考的情况下说出红/黑树中所有可能的旋转情况,或者看看你是否能记住伸展树中所有的 zig/zag 与 zag/zag 情况。从这个意义上说,记住从跳过列表中插入或删除的逻辑会更容易一些。

希望这会有所帮助!

【讨论】:

跳过列表的空间分析忽略了跟踪节点有多少指针所需的空间量。这似乎与 BST 的平衡元数据一样昂贵。【参考方案2】:

而且二叉树不是更容易使用,至少对于搜索而言 (当然,平衡 BST 中的插入和删除可能很棘手)?

树是“更递归的”(树和子树),而 SkipList 是“更迭代的”(数组中的层级)。当然,这取决于实现,但 SkipLists 在实际应用中也非常有用。

在树中搜索更容易,因为您不必在数组中迭代级别。

跳过列表还有其他优点/缺点吗?

跳过列表“更容易”实现。这有点相对,但是实现一个全功能的 SkipList 比在 BinaryTree 中的删除和平衡操作更容易。

树可以是持久的(更适合函数式编程)。

从 SkipLists 中删除项目比二叉树中的内部节点更容易。

向二叉树添加项目更容易(保持平衡是另一个问题)

二叉树是确定性的,因此更容易研究和分析它们。

我的提示:如果你有时间,你必须使用平衡二叉树。如果您的时间不多,请使用跳过列表。如果您没有时间,请使用图书馆。

【讨论】:

【参考方案3】:

目前没有提到的一点是,跳过列表可能有利于并发操作。如果您阅读了由 Doug Lea 撰写的 ConcurrentSkipListMap 的源代码...深入研究 cmets。它提到:

对于搜索树,没有已知的有效的无锁插入和删除算法。索引节点的“向下”链接的不变性(与真实树中的可变“左”字段相反)使得仅使用 CAS 操作即可处理。

【讨论】:

【参考方案4】:

你说得对,这不是一个完美的论坛。

您引用的评论由原始跳过列表论文的作者撰写:不完全是公正的断言。已经 23 年了,红黑树似乎仍然比跳过列表更普遍。 redis key-value pair database 是一个例外,它在其数据结构中包含跳过列表作为选项之一。

跳过列表非常酷。但是我在一般随机情况下能够展示的唯一空间优势是不需要存储平衡标志:每个值两位。这是假设层次结构足够密集以复制二叉树的性能。您可以将其归结为确定性的代价(副随机化)。 SL 的一个很好的特性是您可以使用密度较低的层次结构来用恒定的速度因子换取空间。

旁注:如果您不需要按排序顺序遍历,您可以通过仅加密密钥(即映射到伪随机密码)来随机化 不平衡 二叉树,这并不经常被讨论用 RC4 之类的非常简单的东西的文本)。这样的树实现起来绝对是微不足道的。

【讨论】:

以上是关于平衡二叉树与索引跳过列表的主要内容,如果未能解决你的问题,请参考以下文章

二叉树刷题篇平衡二叉树与二叉树的所有路径

平衡二叉树与java实现

十五、平衡二叉搜索树(Balanced Binary Search Tree)

什么是《平衡二叉树》

排序树与平衡二叉树讲解---新手超级易懂

红黑树与AVL(平衡二叉树)的区别