给出一个TreeNode
的二叉树,如:
class TreeNode {
int data;
TreeNode left;
TreeNode right;
int size;
}
其中size
是(左子树+右子树+ 1)中的节点数。
在O(logn)运行时间中从树中打印一个随机元素。
注意:这篇文章与this之一不同,因为我们清楚地提到我们在此问题中与每个节点都有一个size
。
PS:写这篇文章的灵感来自于this。
答案 0 :(得分:1)
有一种简单的方法可以提供O(n)复杂性。
为了提高复杂性,我们需要在每次迭代时创建自己的排序,而不是像在BFS和DFS中那样顺序排序。我们可以使用每个节点的size
属性来决定是否遍历左子树或右子树。以下是方法:
root.size
范围内的随机数(说r
)r <= root.left.size
,遍历左侧子树r == root.left.size + 1
,打印根(我们找到了要打印的随机节点)r > root.left.size + 1
,遍历右侧子树伪代码看起来像这样:
traverse(root, random)
if r == root.left.size + 1
print root
else if r > root.left.size + 1
traverse(root.right, random - root.left.size - 1)
else
traverse(root.left, random)
以下是java中的实现:
public static void printRandom(TreeNode n, int randomNum) {
int leftSize = n.left == null ? 0 : n.left.size;
if (randomNum == leftSize + 1)
System.out.println(n.data);
else if (randomNum > leftSize + 1)
printRandom(n.right, randomNum - (leftSize + 1));
else
printRandom(n.left, randomNum);
}
答案 1 :(得分:0)
使用尺寸!
在0和n之间选择一个随机数q。
从根开始。如果left->size == q
返回当前节点值。如果left->size < q
向右走,你就走了。如果你右键减去q -= left->size + 1
。重复直到输出节点。
这给你o(树的高度)。如果树是平衡的,则得到O(LogN)。
如果树不平衡但您仍想保留O(logN),则可以执行相同的操作,但限制最大迭代次数。请注意,在这种情况下,并非所有节点都具有相同的返回概率。
答案 2 :(得分:0)
是的,请使用尺寸!
正如Sorin所说,在i
和0
之间选择一个随机数n - 1
(不是n
)
然后执行以下指示:
Treenode rand_node = select(root, i);
select可以如下:
TreeNode select_rec(TreeNode r, int i) noexcept
{
if (i == r.left.size)
return r;
if (i < r.left.size)
return select_rec(r.left, i);
return select_rec(r.right, i - r.left.size - 1);
}
现在这是一个非常重要的技巧:空节点必须是大小设置为0的标记节点,这是有意义的,因为空树的节点为零。您可以避免使用标记,但select()
操作稍微复杂一些。
如果树是平衡的,那么select()
是O(log n)