std :: map已知位置擦除摊销的复杂性和红黑树重新着色的数量

时间:2016-03-17 18:57:27

标签: c++ time-complexity erase red-black-tree amortized-analysis

std::map::erase(iterator)的复杂性已分摊 O(1)(例如,请参阅here)。虽然标准库没有规定实现,但事实上这意味着红黑树所需的重新平衡操作的数量是分摊的 O(1)。事实上,维基百科上的红黑树seems to confirm this条目:

  

恢复红黑属性需要少量(O(log n)或摊销的O(1))颜色变化(实际上非​​常快)和不超过三次树旋转(两次插入)。

但似乎没有链接(我无法在其他地方找到它)。

由于转数是常数,因此摊销是在节点根路径上所需的重新着色次数。虽然平衡树中的大多数节点都朝向树的底部(因此平均路径是对数的),但它显然是分摊的 O(1),这是令人惊讶和有趣的。如何证明摊销的固定成本?

1 个答案:

答案 0 :(得分:4)

我会在这个答案中假设您熟悉摊销分析,并对银行家的方法感到满意。我还假设你知道红黑树不变量。

对于一些小的常数k,简短的回答是,将k个硬币放在每个没有一个红色孩子的黑色节点上。

请注意,红黑树中有许多不同的删除算法。使用迭代器擦除显然需要一种自下而上的算法。此处的分析假设算法大致如下:

  1. 向上移动直到找到黑色节点。这总是可行的,因为根是黑色的,并且它永远不会超过两个跳,因为红色节点不能有红色的孩子。

  2. 在O(1)时间内以该黑色节点为根的子树上执行“fixup”操作。如果fixup降低了子树的高度或者将根的颜色从黑色变为红色,则向根部移动一步并返回到#1。

  3. 需要做一些工作才能看到#2是可能的。事实上,这种复杂性是塞奇威克左倾红黑树的动机之一。这主要是一个枚举所有案例,进行单轮或双轮旋转,然后仔细检查你没有违反任何不变量的问题。

    修复操作的一个变体(如果您已经有另一个有效变体,则不难发现)在遍历树的过程中保留两个额外的不变量:

    1. 当子树的高度减少1时,子树(a)的根最初有两个黑人孩子(b)现在只有一个红孩子。

    2. 子树永远不会将颜色从黑色变为红色。

    3. 因此,对于遍历的每一步,

      1. 子树的根有一个或两个红孩子。我们执行O(1)工作,最多添加O(1)个硬币,并停止

      2. 我们执行O(1)工作,通过将一个有两个黑人孩子的节点变成一个有一个红孩子的节点来取回O(1)个硬币,然后继续

      3. 案例#2 免费摊销,只要硬币数量足以支付重组和重新加载的费用#2。因此,删除的总摊还成本是我们在单个删除操作中遇到情况#1的次数,最多只有一次,因为在我们点击之后我们停止了。

        虽然这涵盖了解释算法的机制,但它并没有真正解释为什么删除是分摊O(1)。

        学生有时会教授有关摊还成本的案例之一就是递增二进制数。在最坏的情况下,成本是Ω(lg n),但在摊销意义上是O(1),通过在每个'1'数字上放置恒定数量的硬币。

        类似地,递减是O(1)通过在每个'0'数字上放置恒定数量的硬币来摊销。然而,混合两者使每个成本Ω(lg n),即使在摊销设置中,因为摊销分析取决于所有遍历步骤,除了最后给出恒定数量的硬币。

        这种遍历是免费的,直到你停止的主题类似于上面的红黑树分析。必须放置硬币的数字是表示即将进行的结构调整的数字。使用物理学家的方法,将潜在的能量添加到每个数字的结构中。

        考虑二进制数的不同表示,其中数字可以是0,1,或2 (但数字d_i仍代表d_i * 2 ^ i)。这称为冗余二进制。现在,您可以在所有0或2位数上放置一定数量的硬币,并获得摊销的常数时间增量和减量。原因是级联递增或递减会将0或2更改为1,因此总是会获得硬币。

        因此,对于两位数,递增或递减是O(1)摊销,但不是两者都有,并且有三个,两者都可以摊销O(1)。

        同样,插入或删除(但不是两者)在所有的情况下摊销O(1):

        1. 红黑树,其中黑色节点只能有一个红色的孩子

        2. AA-树

        3. 2-3棵树

        4. (a,2a-1)树,对于任何a> 1。

        5. 虽然插入和删除都是O(1)在红黑树,(2,4)树和(a,2a)树中分摊,但任何a>> 1。