通过序数索引访问红黑树

时间:2012-03-31 06:20:25

标签: algorithm binary-tree complexity-theory red-black-tree

我有一棵红黑树(二叉树,所有树叶都在2级以内)。 我可以浏览节点:左,右或父。 我知道整个节点数。

我必须找到树中的第N个最小元素。有没有办法比O(n)更快地做到这一点?是否有通过索引优化访问的想法?

3 个答案:

答案 0 :(得分:3)

在每个节点X中,您应该存储多少个节点在子树中,X作为根。

count(LEAF) = 1
count(NODE) = count(NODE->LEFT) + count(NODE->RIGHT) + 1

在每次插入/删除期间,您应使用此公式更新受旋转影响的节点中的计数。

之后解决方案很简单

NODE nth(NODE root, int n) {
    if (root->left->count <= n) return nth(root->left, n);
    if ( root->left->count + 1 == n) return root;
    return nth(root->right, n - root->left->count - 1);
}

答案 1 :(得分:2)

您可以在每个节点中添加一个属性,该属性显示此节点的子节点数。 使用此属性,您可以找到具有O(lgn)的第N个最小节点。

现在只需要在将任何节点插入(或删除)到树中时处理此属性。 如果没有旋转那么它很容易处理,但是当你有旋转时,它有点困难,但你可以做到。

答案 2 :(得分:1)

对于红黑树,您不需要跟踪左侧的节点数量,因为如果它是正确偏置的(应该是),那么左节点的数量将始终形成一个mersenne系列(1,3,7,15, 31 ...)或2^depth -1

考虑到这一点,我们可以写下递归获取节点的逻辑。 上面接受的答案已切换标志。这是elixir中的正确实现。对于package

def nth(%Rbtree{node: r}, n), do: do_nth(r, n)
defp do_nth({_,h,k,v,l,r}, n) do
  l_count = left_count(h)
  cond do
    l_count > n ->
      case l do
        nil -> {k,v}
        _ -> do_nth(l, n)
      end
    l_count == n -> {k,v}
    true ->
      case r do
        nil -> {k,v}
        _ -> do_nth(r, n - l_count - 1)
      end
  end
end
defp left_count(1), do: 0
defp left_count(0), do: 0
defp left_count(h), do: :math.pow(2,h-1)-1 |> round