问题:STL红黑树(stl_tree.h)是否按顺序迭代时间复杂度为O(N ln N)? 我在网上搜索,找不到答案。
我认为任何ADT的有序迭代的时间复杂度应该是O(N)。如果我错了,请告诉我。
我从这段代码中查看了STL RB树 (https://www.sgi.com/tech/stl/stl_tree.h)
迭代器的++运算符似乎不是O(1)而是O(ln N)。
void _M_increment()
{
if (_M_node->_M_right != 0) {
_M_node = _M_node->_M_right;
while (_M_node->_M_left != 0)
_M_node = _M_node->_M_left;
}
else {
_Base_ptr __y = _M_node->_M_parent;
while (_M_node == __y->_M_right) {
_M_node = __y;
__y = __y->_M_parent;
}
if (_M_node->_M_right != __y)
_M_node = __y;
}
}
如果我没有记错,上面的代码是O(ln N)复杂度而不是O(1)。
然后,以下++运算符也将具有O(ln N)复杂度。
_Self& operator++() { _M_increment(); return *this; }
_Self operator++(int) {
_Self __tmp = *this;
_M_increment();
return __tmp;
}
这意味着即使是STL RBTree上的简单迭代也将是O(N ln N)而不是O(N)。
我错了还是做了一些奇怪的假设?
顺便说一下,我想到了基于堆栈的迭代器堆叠路径。 我认为它可以实现O(1)时间复杂度,但它会花费O(ln N)空间复杂度,就像基于递归的有序遍历会花费一样。
然而,堆栈方法的问题是当不同的线程改变树结构并通过旋转弄乱路径堆栈时。 (但是大多数情况下,当我们考虑使用这种类型的ADT进行多线程编程时,我们通常会锁定整个树,因此路径搞乱并不是一个大问题......对吗?)即使是这种类型的O (ln N)方法无论如何都不是线程安全的。
提前致谢。
答案 0 :(得分:2)
_M_increment在最坏的情况下是O(lg n),是的,但是摊销O(1)。您可以访问维基百科以了解有关Amortized Analysis的更多信息。
我在这里给出一个直观的解释,而不是证明:
考虑树中的每个边缘。对于每个边缘,它将在整个遍历中使用两次,一次用于输入,一次用于离开。
边数是O(n)。在_M_increment中,我们最关心的部分是循环。幸运的是,这些循环消耗了边缘访问,这意味着,对于整个遍历,循环体将总共执行O(n)次。所以每个增量的摊销复杂度是O(1)。