【问题标题】:Is there any boost/stl container which supports the following operation?是否有任何支持以下操作的 boost/stl 容器?
【发布时间】:2014-04-15 19:38:02
【问题描述】:

我正在寻找可以提供以下功能的 stl/boost 容器:

  1. 按排序顺序自动插入元素。 (log n)
  2. 从起始节点返回元素的索引/深度。 (log n)

如果没有,实现这一目标的最佳方法是什么?我正在考虑使用双向链接列表的解决方案。这是解决这个问题的好选择吗?

【问题讨论】:

  • 有序双向链表的插入成本为 O(#list/2)。反正为什么一定要排序呢?
  • 一个创建良好的容器类将被平衡,因此深度不会与海量数据集无关。在这一点上,您可能会更领先于使用哈希
  • 我对 CS 世界或 'O(n) etc' 世界一无所知,但对我来说,这听起来像是一个树容器而不是双链表。
  • 在现实生活中通常用 O(log(n)) 插入比用 O(1) 插入要慢,最后用 O(nlog(n)) 排序。我总是只使用向量和标准快速排序
  • 第一个要求意味着你需要一个关联容器。但是在 2. 你需要 O(logN) 查找还是随机访问?换句话说,你是想找到一个元素还是跳一些元素?

标签: c++ c++11 boost stl


【解决方案1】:

更新

您需要order statistic tree。 C++ 标准库没有,也没有提供简单的实现方法,请参阅

boost 也没有,请参阅Future work 和上面链接的问题。

不过,好消息是,libstdc++ 中提供了这样的树作为扩展!


(原答案:)

  1. 按排序顺序自动插入元素。 (log n)
  2. 从起始节点返回元素的索引/深度。 (log n)

在我看来,C++ 标准库和 boost 都没有提供可以开箱即用地提供这些复杂性保证的容器。您要么必须自己实现此容器,要么放宽您的复杂性要求并允许O(n) 用于其中至少一个。

如果不是:实现这一目标的最佳方法是什么?我在想一个 使用双链表的解决方案。解决这个问题会是不错的选择吗 问题?

std::list 是一个双向链表,但只能实现线性时间插入。 std::list 是一个很大的性能杀手,因为它对缓存的使用不当。

boost::container::flat_set 可能会更好,它也只提供线性时间插入,但由于缓存的出色使用(感谢硬件预取器),它的速度仍然可能会让您感到惊讶。作为奖励,您可以获得随机访问迭代器,因此如果您已经拥有该元素,则可以在 O(1) 时间找到索引。

如果两个复杂性要求都是必须的,那么我认为没有比实现自平衡二叉搜索树并在每个父节点上存储子树大小更简单的方法了。维护这些额外信息不会破坏O(log n) 的复杂性。即使您从std::mapred-black tree 实现之一开始实现它也是一项重要且非平凡的工作(不保证是红黑树,但在libstdc++ 中它是并且它是开放的-来源)。


又想到了一件事情:您的使用模式是什么?您是否一个接一个地完全随机地进行插入和索引查找?如果不是,或者至少主要不是,那么您可能会在两者之间切换数据结构并摆脱 stl 或 boost 容器之一。

【讨论】:

  • "从起始节点返回元素的索引/深度。"是一项功能,而不是复杂性保证
  • 保证在 std::set、std::map 等中的插入和查找具有 O(logN) 复杂度,并且迭代器在排序范围内进行迭代。
  • @sehe 他希望在O(log n) 时间内做到这一点,这是一个复杂性要求。在O(n) 时间使用std::setstd::map 很容易做到这一点,所以在我看来std::setstd::map 有这个功能,但没有这个复杂性保证。现在是否清楚我的意思或我应该修改答案?
  • @AdamWulkiewicz 问题是您仍然需要O(n) 时间来计算密钥的等级。只有当您有随机访问迭代器时,排序范围才会有所帮助。请参阅我的相关问题Is there any technical reason why std::lower_bound is not specialized for red-black tree iterators?
  • @Ali 是的。我正在随机进行插入和查找。我想插入一个值,然后在每一步之后按排序顺序找出该值的索引。
【解决方案2】:

std::mapstd::set根据标准保证O(log(N))的插入和搜索,它们也满足排序条件。请参阅section 23.4 上的 C++ 标准。

@StefanoSanfilippo 建设性评论后更新

但请记住,这些容器只允许唯一的键/元素。如果您有多个值,则必须求助于std::multimapstd::multiset。这些容器与std::mapstd::set 具有几乎相同的属性,但允许多个键/元素。

现在关于 STL 容器的索引/深度问题,不能保证 std::mapstd::set 被实现为二叉树,因此没有用于访问树属性(例如深度和索引(请参阅How to find the depth of each node in std::map?)。做出有根据的猜测,我认为 boost 的树状容器也是如此。

更新 - 引用 @Mooing Duck 的评论:

boost 树也没有办法获取索引。

【讨论】:

  • std::set 对 OP 可能无法接受的元素的唯一性有限制。
  • @StefanoSanfilippo 感谢您的建设性评论,我会添加更新。
  • boost 树也没有办法获取索引,虽然我写了一个基于索引的树容器,所以是可行的。
  • @40two 如果我使用多重设置。查找索引将变为 o(n)。
  • @VivekGoel 它是对数的,参见 C++ 标准 [isocpp.org/files/papers/N3797.pdf] 第 743 页,表 102。
【解决方案3】:

二叉树(B-tree)在平均情况下具有对数插入和检索(相当于深度计数)时间,并且在按顺序遍历时自然排序。

不幸的是,如果树不平衡(最坏的情况),B 树的插入/检索时间可能会退化为线性。如果这是一个问题,您应该考虑使用红黑树,它不会消除问题,但会减轻问题,同时保持插入时间与树的大小成对数。

STL 和 Boost 都没有实现 B- 或 RB- 树,尽管std::map通常被实现为 RB。

请记住,链表的插入时间是线性的,即使是双倍的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-04
    • 2017-11-12
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多