【问题标题】:How is the ordering of std::map achieved?std::map 的排序是如何实现的?
【发布时间】:2015-01-31 12:31:53
【问题描述】:

我们可以从几个来源看到 std::map 是使用红黑树实现的。据我了解,这些类型的数据结构不会以任何特定的顺序保存其元素,而只是保持 BST 属性和高度平衡要求。

那么,为什么 map::begin 是常数时间,并且我们能够迭代有序序列?

【问题讨论】:

  • @Matt Courbrough 这表示 std::map 保证被排序,而不是如何使用红黑树来实现。
  • 可能误解了您的问题,但这些答案说明了用于订购的比较功能。
  • DietrichEpp 的回答涵盖了排序方面。关于“那么,map::begin 是如何成为常数时间的”——而不是在调用 begin 时通过树下降(将是 O(log2N)),实现跟踪(它在内部选择指针或迭代器——无论如何最终可能是相同的事情 - 但begin 本身显然会返回一个迭代器)当前树中的最低值节点,如果需要在eraseinsert 等之后更新它 - 所以它可以是在恒定时间内提供。最后一个节点将被类似地跟踪(对于rbegin())。
  • @Tony D 这对于开始迭代器是有意义的。我猜它一定是在做一些事情,比如遍历并在某个时候对其进行排序以获得序列的其余部分?

标签: c++ data-structures


【解决方案1】:

std::map 内部维护 BST 的前提开始(标准没有严格要求,但大多数库可能会这样做,就像红黑树一样)。

在 BST 中,要找到最小的元素,您只需沿着左分支直到到达叶子,即 O(log(N))。但是,如果您想在恒定时间内交付“begin()”迭代器,只需在内部跟踪最小元素就非常简单。每次插入导致最小元素发生变化时,您都会更新它,仅此而已。当然,这是内存开销,但这是一种权衡。

可能还有其他方法可以挑选出最小的元素(比如故意保持根节点不平衡)。无论哪种方式,都不难做到。

要遍历“有序”序列,您只需按顺序遍历树。从最左边的叶子节点开始,你去(上),(右),(上,上),(右),......等等..这是一套简单的规则,很容易实现,只需@ 987654321@ 我不久前写的。当您进行中序遍历时,您将按照正确的顺序从最小到最大访问每个节点。换句话说,它只是给你“数组”已排序的错觉,但实际上,是遍历使它看起来像是已排序。

【讨论】:

  • 谢谢,我错过了产生排序顺序的中序遍历部分。
  • @SteveM 在旁注中,像std::map 这样的容器并不擅长迭代。树遍历的逻辑,加上你一直在内存中跳转的事实,使得在实践中的性能很差。因此,请考虑将它们用于“主要查找,偶尔的有序遍历”或查找值范围之类的事情。对于快速遍历,您通常最好维护一个已排序的std::vectorMy diagram 提到了这一点。
【解决方案2】:

红黑树的平衡特性允许您以 O(log N) 的成本在树中的任何位置插入一个节点。对于典型的std::map 实现,容器会保持树的排序,每当插入新节点时,将其插入正确的位置以保持树的排序,然后重新平衡树以保持红黑属性。

所以不,红黑树本身并不是排序的。

【讨论】:

    【解决方案3】:

    RB 树是二叉搜索树。二叉搜索树不一定以任何特定的顺序存储它们的元素,但你总是可以得到一个中序遍历。我不确定 map::begin 如何保证恒定时间,我假设这涉及始终记住最小元素的路径(通常是 O(log(n)))。

    【讨论】:

      猜你喜欢
      • 2014-08-03
      • 2015-12-04
      • 1970-01-01
      • 2013-05-08
      • 2011-04-14
      • 2016-11-21
      • 2019-05-03
      • 2013-01-05
      • 2013-11-01
      相关资源
      最近更新 更多