我有一棵红黑树(二叉树,所有树叶都在2级以内)。 我可以浏览节点:左,右或父。 我知道整个节点数。
我必须找到树中的第N个最小元素。有没有办法比O(n)更快地做到这一点?是否有通过索引优化访问的想法?
答案 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