在O(logn)

时间:2016-01-11 05:03:36

标签: java algorithm random binary-tree

给出一个TreeNode的二叉树,如:

class TreeNode {
    int data;
    TreeNode left;
    TreeNode right;
    int size;
}

其中size是(左子树+右子树+ 1)中的节点数。

  

在O(logn)运行时间中从树中打印一个随机元素。

注意:这篇文章与this之一不同,因为我们清楚地提到我们在此问题中与每个节点都有一个size

PS:写这篇文章的灵感来自于this

3 个答案:

答案 0 :(得分:1)

有一种简单的方法可以提供O(n)复杂性。

  • 生成1到root.size
  • 范围内的随机数
  • 执行BFSDFS遍历
  • 停止在random numbered元素处迭代并打印它。

为了提高复杂性,我们需要在每次迭代时创建自己的排序,而不是像在BFS和DFS中那样顺序排序。我们可以使用每个节点的size属性来决定是否遍历左子树或右子树。以下是方法:

  • 生成1到root.size范围内的随机数(说r
  • 从根节点开始遍历并决定是否转到左子树,右子树或打印根:
    • if r <= root.left.size,遍历左侧子树
    • if r == root.left.size + 1,打印根(我们找到了要打印的随机节点)
    • if r > root.left.size + 1,遍历右侧子树
  • 基本上,我们已经定义了当前节点的排序顺序(当前左子树的大小)+ 1。
  • 由于我们在每次迭代时消除了遍历左或右子树的遍历,因此其运行时间为O(logn)。

伪代码看起来像这样:

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所说,在i0之间选择一个随机数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)