C++ : Running time of next() and prev() in a multiset iterator?

标签 c++ algorithm c++11 binary-search-tree multiset

应用 next() 的时间复杂度是多少?和 prev() multiset<int>::iterator 上的函数类型对象,其中对应的多重集包含 N元素?

我知道在 STL 中,多重集被实现为平衡的二叉搜索树,因此我希望每次操作的时间复杂度为 O(log N)(在最坏的情况下),以防我们只是遍历树直到我们找到合适的值,但我有预感这应该是平均 O(1)。

但是如果树的实现如下-插入元素时x在平衡二叉搜索树中,我们还可以检索到树中小于 x 的最大数和大于 x 的树中的最小数。在 O(log N) 中。因此理论上,我们可以让树中的每个节点都维护指向其next 的指针。和 prev元素,以便 next()prev()然后在每个查询中以恒定时间运行。

有人可以分享一下发生了什么吗?

最佳答案

标准要求迭代器上的所有操作都在摊销的常数时间内运行:http://www.eel.is/c++draft/iterator.requirements#general-10 .基本思想是每个迭代器类别只定义可以在摊销时间内实现的操作。

迭代是很常见的事情,如果迭代器上的 operator++ (我猜这就是你的意思是下一个?)是 logN,那么在循环中遍历容器将是 NlogN。该标准使这成为不可能;由于 operator++ 是摊销常数,因此迭代标准中的任何数据结构总是 O(N)。

但是,我在 gcc5.4 上研究了 multiset 的实现,至少有一个例子。 setmultiset 都是根据相同的底层结构 _Rb_tree 实现的。深入研究一下这个结构,它的节点不仅有左右节点指针,还有父节点指针,而迭代器只是指向节点的指针。

给定二叉搜索树中包含指向其父节点的指针的节点,很容易找出树中的下一个节点是什么:

  1. 如果它有一个右 child ,则下降到右 child 。然后尽可能下降左 child ;这是下一个节点。
  2. 如果它没有右子节点,则上升到父节点,并确定原始节点是父节点的左子节点还是右子节点。如果节点是父节点的左子节点,则父节点是下一个节点。如果节点是父节点的右边,则父节点已经被处理,因此您需要在父节点与其祖父节点之间递归地应用相同的逻辑。

这个 SO 问题显示了具有核心逻辑的源代码:What is the definition of _Rb_tree_increment in bits/stl_tree.h? (由于某种原因很难找到)。

这没有恒定的时间,特别是在 1. 和 2. 我们有下降或上升的循环,最多可能需要 log(N) 时间。但是,您可以轻松地说服自己摊销时间是常数,因为当您使用此算法遍历树时,每个节点最多被触摸 4 次:

  1. 曾经在它的左边 child 的路上。
  2. 一旦它从左 child 那里回来,需要考虑自己。
  3. 当它下降到它的右 child 时。
  4. 从右 child 升序时一次。

回想起来,我会说这是一个相当明显的选择。对整个数据结构的迭代是一种常见的操作,因此性能非常重要。添加第三个指向节点的指针不是微不足道的空间量,但也不是世界末日;最多它会使数据结构从 3 个字膨胀到 4 个字(2 个指针 + 数据,对齐最少为 3 个,而 3 个指针 + 数据)。如果您使用范围,而不是两个迭代器,另一种方法是维护一个堆栈,然后您不需要父指针,但这仅在您从头到尾迭代时才有效;它不允许从中间的迭代器迭代到结尾(这也是 BST 的重要操作)。

关于C++ : Running time of next() and prev() in a multiset iterator?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46119328/

相关文章:

c++ - 在循环外定义变量

c++ - 动态创建的自定义 QML 对象不可见

algorithm - 数递增算法效率

algorithm - 一种创建拼图的快速算法

c++ - 在 C++ 高效存储上,刷新文件策略

c++ - g++ 4.9 sanitizer 错误,在 linux 上使用 cin 解析 bool 值(ubuntu 12.04 64 位)

c++ - 在 DMA 之后反向更改为 int

algorithm - 计算网格中的连接点

c++ - 错误 :sorry, 未实现:在函数模板中使用 decltype 时,函数模板签名中存在字符串文字

c++ - 什么可以使 std::map 找不到它的键之一?