![](/img/trans.png)
[英]std::map Known-Position Erase Amortized Complexity And Number of Red-Black Tree Recolorings
[英]Why is std::map implemented as a red-black tree?
这真的取决于用法。 AVL 树通常具有更多的重新平衡旋转。 因此,如果您的应用程序没有太多的插入和删除操作,但重在搜索,那么 AVL 树可能是一个不错的选择。
std::map
使用红黑树,因为它在节点插入/删除和搜索的速度之间获得了合理的权衡。
先前的答案仅针对树替代方案,而红黑可能仅出于历史原因而保留。
为什么不是哈希表?
类型只需要<
运算符(比较)用作树中的键。 但是,哈希表要求每个键类型都定义了一个hash
函数。 将类型要求保持在最低限度对于泛型编程非常重要,因此您可以将其与各种类型和算法一起使用。
设计一个好的哈希表需要深入了解它将被使用的上下文。 它应该使用开放寻址还是链接链接? 在调整大小之前它应该接受什么级别的负载? 它应该使用避免冲突的昂贵散列,还是粗略快速的散列?
由于 STL 无法预测哪个是您的应用程序的最佳选择,因此默认值需要更加灵活。 树木“正常工作”并且可以很好地扩展。
(C++11 确实添加了带有unordered_map
哈希表。您可以从文档中看到它需要设置策略来配置许多这些选项。)
其他树呢?
与 BST 不同,红黑树提供快速查找和自我平衡。 另一位用户指出了它相对于自平衡 AVL 树的优势。
Alexander Stepanov(STL的创造者)说如果std::map
他会用B*树而不是红黑树,因为它对现代内存缓存更友好。
从那时起最大的变化之一就是缓存的增长。 缓存未命中成本非常高,因此引用的局部性现在更为重要。 基于节点的数据结构具有较低的引用局部性,意义不大。 如果我今天设计 STL,我会有一组不同的容器。 例如,在实现关联容器时,内存中的 B* 树是比红黑树更好的选择。 —— 亚历山大·斯捷潘诺夫
地图应该总是使用树吗?
另一种可能的映射实现是排序向量(插入排序)和二分搜索。 这对于不经常修改但经常查询的容器很有效。 我经常在 C 中这样做,因为qsort
和bsearch
是内置的。
我什至需要使用地图吗?
缓存考虑意味着即使对于我们在学校教过的那些情况(例如从列表中间删除一个元素),在std:vector
上使用std::list
或std::deque
也很少有意义。 应用相同的推理,使用 for 循环线性搜索列表通常比为几次查找构建地图更有效和更清晰。
当然,选择一个可读的容器通常比性能更重要。
AVL 树的最大高度为 1.44logn,而 RB 树的最大高度为 2logn。 在 AVL 中插入元素可能意味着在树中的某一点重新平衡。 重新平衡完成插入。 插入新叶子后,必须更新该叶子的祖先直到根,或者直到两个子树的深度相等的点。 必须更新 k 个节点的概率是 1/3^k。 重新平衡是 O(1)。 删除一个元素可能意味着不止一次重新平衡(最多为树的深度的一半)。
RB 树是表示为二叉搜索树的 4 阶 B 树。 B 树中的 4 节点导致等效 BST 中的两个级别。 在最坏的情况下,树的所有节点都是 2 个节点,只有一个 3 个节点的链直到一个叶子节点。 那片叶子与根的距离为 2logn。
从根向下到插入点,必须将 4-nodes 更改为 2-nodes,以确保任何插入都不会使叶子饱和。 从插入回来后,必须分析所有这些节点以确保它们正确表示 4 节点。 这也可以在树中向下完成。 全球成本将是相同的。 天下没有免费的午餐! 从树中删除元素的顺序相同。
所有这些树都要求节点携带高度、重量、颜色等信息。只有 Splay 树没有这些附加信息。 但是大多数人都害怕 Splay 树,因为它们的结构过于随意!
最后,树还可以在节点中携带权重信息,从而实现权重平衡。 可以应用各种方案。 当一棵子树包含的元素数量超过另一棵子树的 3 倍时,应该重新平衡。 再次通过单轮或双轮进行重新平衡。 这意味着 2.4logn 的最坏情况。 可以使用 2 次而不是 3 次,这是一个更好的比率,但这可能意味着在这里和那里留下不到 1% 的子树不平衡。 棘手!
哪种树最好? 肯定是AVL。 它们最容易编码,并且最接近 logn 的高度。 对于包含 1000000 个元素的树,AVL 最多为 29,RB 为 40,权重平衡为 36 或 50,具体取决于比率。
还有很多其他变量:随机性、添加、删除、搜索的比率等。
2017 年 6 月 14 日更新:webbertiger 在我发表评论后编辑其答案。 我应该指出,它的答案现在对我来说好多了。 但我保留了我的答案作为附加信息......
由于我认为第一个答案是错误的(更正:不再是两个答案),而第三个答案是错误的。 我觉得我必须澄清一些事情......
两种最受欢迎的树是 AVL 和红黑 (RB)。 主要区别在于利用率:
主要区别来自着色。 RB 树中的重新平衡操作确实比 AVL 少,因为着色使您有时可以跳过或缩短成本相对较高的重新平衡操作。 由于着色,RB 树还具有更高级别的节点,因为它可以接受黑色节点之间的红色节点(有可能增加约 2 倍的级别),从而使搜索(读取)效率稍低……但因为它是一个常数 (2x),它保持在 O(log n) 中。
如果您考虑修改树的性能损失(有意义的)VS 咨询树的性能损失(几乎无关紧要),那么在一般情况下更喜欢 RB 而不是 AVL 就变得很自然了。
这只是您实现的选择 - 它们可以作为任何平衡树来实现。 各种选择都具有可比性,但差异很小。 因此,任何都和任何一样好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.