从二叉树中随机选择一个节点

时间:2015-08-14 13:25:55

标签: java binary-tree

我的树类:

public class BT<E>{
   E value;
   BT<E> left, right;

   public BT(E value)
   {
      this.value=value;
   }   

   public BT (E value, BT left, BT right) 
   {
      this.value = value;
      this.left = left;
      this.right = right;
   }

在生成树后,如何从树中返回随机节点?我知道我生成的每棵树的节点的深度和数量。

4 个答案:

答案 0 :(得分:6)

因此,使用Random Integer方法并返回0和树大小之间的整数。

然后在树上进行广度/深度优先遍历,使用计数器,当节点到达随机整数时返回该节点。

Random rand = new Random();
int randomNum = rand.nextInt(treesize);
int count = -1;
Node randNode;

public static void getRandomTraversal(Node root){

    count++;

    if(count == randomNum)
        randNode = root;

    if(root.leftChild != null)
        getRandomTraversal(root.leftChild);

    if(root.rightChild != null)
        getRandomTraversal(root.rightChild);
}

或者,您可以将count删除为全局,并将其作为参数添加到递归函数中。虽然当树有多个孩子时,二元树就不那么容易了。

答案 1 :(得分:4)

Dennis和Jeroen的算法很容易实现,但O(n)。我相信我的O(log n)算法稍微复杂一些。

每个节点都需要有相同的选择机会。因此,在某个树T,让LN(T)为左树中的节点数,RN(T)为右树中的节点数,N(T)为数字总节点数,包括这个节点(所以N(T) = 1 + LN(T) + RN(T))。从R中选择一个随机数0 to N(T) - 1。如果R == 0,则返回此节点。如果是1 <= R <= LT(N),请在左子树上递归此方法。否则,请在右子树上递归此方法。

未经测试的代码(假设BT的{​​{1}}方法适用于.size()):

O(1)

当然,您可以执行诸如将public BT randomNode() { int r = new Random().nextInt(this.size()); if (r == 0) { return this; } else if (left != null && 1 <= r && r <= left.size()) { return left.randomNode(); } else { return right.randomNode(); } } 提升出方法之类的操作,但算法是相同的。

编辑:当左子树为空时修复空指针异常。

答案 2 :(得分:3)

  1. 选择一个随机数(new Random().nextInt(numberOfNodes)
  2. 走过你的树,但你的喜欢(深度优先,广度优先,后序,有序,预订)
  3. 对于您访问的每个节点,增加一个计数器
  4. 如果计数器的值等于随机选择的数字,请选择该节点

答案 3 :(得分:0)


首先

问题也出在书<Cracking the coding interview 6th>的{​​{1}}部分| IX Interview Questions | 4. Trees and graphs

(这是Google的一位软件工程师Question 4.11写的一本好书,他采访了很多人。)


算法

  • (A)决定随机数,并记录子树大小。

    逻辑:

    • Impl二叉树节点。

      • 每个节点都有一个大小字段。
        指示以该节点为根的子树大小。
      • 如果每个节点都有一个父字段,则指向它的父节点很有帮助。
    • 简单的二叉树,最好是BST。

      • 在插入/删除时,维护节点的大小和父字段。
    • BSTNode getRandomNode(BSTNode当前,整数兰特)
      比较rand与子树大小:

      Gayle Laakmann McDowell
    • T getRandomNode(BST树)
      • 如果树为空,则返回null。
      • 生成一个随机数,范围为Node<T> left = current.getLeft(); // left, int leftSize = (left == null ? 0 : left.getSize()); // get size of left subtree, if (rand == leftSize) return current; // current is chosen, if (rand < leftSize) return randomNode(current.getLeft(), rand); // call on left subtree recursively, else return randomNode(current.getRight(), rand - leftSize - 1); // call on right subtree recursively,
      • 呼叫[0, size),并获得随机节点。
      • 返回随机节点的值。

    复杂度:

    • 时间:getRandomNode(tree.getRoot(), rand)
    • 空格:O(h),(方法堆栈使用O(1)

    位置:

    • O(h)是树高。
  • (B)放入列表,可通过随机索引访问。

    逻辑:

    • 将树节点添加到列表中,以支持通过索引进行快速访问。
    • 调用该方法时,生成一个h范围内的随机索引,然后在给定索引处获取节点。

    复杂度:

    • 时间:[0, size) (如果树是静态的)

    • 空格:O(1)

    位置:

    • O(n)是树的大小。

    提示

    • 如果树是动态的,则在树更改时还需要维护数组。
      • 从树中删除节点时,也需要从数组中删除。
        这也需要n
      • 将节点添加到树时,还需要追加到数组。
        如果结尾处留有空白,这将花费O(n),但是在数组调整大小时,它是O(1)

比较2种算法:

  • 对于动态树,O(n)使用较少的内存,并且运行速度更快。肯定是赢家。
  • 对于静态树,也可以考虑使用A,在建立数组之后,在循环时它可能会更快。

代码

(以下是在B中使用上述算法Java和测试用例的实现。)

RandomBST.java

A

RandomBSTTest.java (部分)
(单元测试,通过import java.util.concurrent.ThreadLocalRandom; /** * Random BST impl. * * @param <T> * @author eric * @date 2/10/19 5:02 PM */ public class RandomBST<T extends Comparable> extends BST<T> implements RandomBSTDesign<T> { @Override public void add(T v) { root = add((RandomBSTNode<T>) root, v, (RandomBSTNode<T>) root); } /** * Add to a subtree start from given node. * * @param current root of a subtree to add node to, * @param currentParent parent of current, * @param v * @return the new root of subtree, */ protected RandomBSTNode<T> add(RandomBSTNode<T> current, T v, RandomBSTNode<T> currentParent) { if (current == null) { // subtree is empty, RandomBSTNode<T> newNode = new RandomBSTNode<>(v, currentParent); // create new node, newNode.incSizeOfParents(); // increase size of parents, size++; return newNode; } // compare, int compareFlag = v.compareTo(current.value); // check this or subtree, if (compareFlag < 0) { // smaller, go to left subtree, current.left = add(current.getLeft(), v, current); } else if (compareFlag > 0) { // larger, go to right subtree, current.right = add(current.getRight(), v, current); } else { // equals, ignore it, } return current; } @Override protected RandomBSTNode<T> delete(BSTNode<T> current, T v) { return delete((RandomBSTNode<T>) current, v); // cast to subtype to call another method, } /** * Delete given value in subtree, if any. * * @param current * @param v * @return new root of the subtree */ protected RandomBSTNode<T> delete(RandomBSTNode<T> current, T v) { if (current == null) return null; // compare, int compareFlag = v.compareTo(current.value); // check this or subtree, if (compareFlag < 0) { // smaller, go to left subtree, current.left = delete(current.left, v); } else if (compareFlag > 0) { // larger, go to right subtree, current.right = delete(current.right, v); } else { // equals, delete it, // no child, if (current.left == null && current.right == null) { current.decSizeOfParents(); // decrease size of parents, size--; return null; } // one child - right, if (current.left == null) { current.decSizeOfParents(); // decrease size of parents, size--; RandomBSTNode<T> randomRight = current.getRight(); // cast to subtype, randomRight.parent = current.parent; return randomRight; } // one child - left, if (current.right == null) { current.decSizeOfParents(); // decrease size of parents, size--; RandomBSTNode<T> randomLeft = (RandomBSTNode<T>) current.left; // cast to subtype, randomLeft.parent = current.parent; return randomLeft; } // 2 child, T smallestInRight = findSmallestValue(current.right); current.value = smallestInRight; // replace current with smallest in right subtree, current.right = delete(current.right, smallestInRight); // delete original smallest value in right subtree, } return current; } @Override public RandomBSTNode<T> getRoot() { return (RandomBSTNode<T>) root; } @Override public T randomValue() { if (size == 0) return null; // empty tree, int rand = ThreadLocalRandom.current().nextInt(size); // random value, RandomBSTNode rnode = randomNode(getRoot(), rand); // get random node, // System.out.printf("rand = %d, random node: %s\n", rand, rnode); return rnode == null ? null : (T) rnode.value; } protected RandomBSTNode randomNode(RandomBSTNode<T> current, int rand) { RandomBSTNode<T> left = current.getLeft(); // left, int leftSize = (left == null ? 0 : left.getSize()); // get size of left subtree, if (rand == leftSize) return current; // current is chosen, if (rand < leftSize) return randomNode(current.getLeft(), rand); // call on left subtree recursively, else return randomNode(current.getRight(), rand - leftSize - 1); // call on right subtree recursively, } /** * Random BST node impl. * * @param <T> */ static class RandomBSTNode<T extends Comparable> extends BSTNode<T> implements RandomBSTNodeDesign<T> { protected int size; // size of subtree which has this node as root, protected RandomBSTNode<T> parent; // parent of this node, or null if this is root, public RandomBSTNode(T value, RandomBSTNode<T> parent) { this(value, 1, parent); // on creation, got size 1, } public RandomBSTNode(T value, int size, RandomBSTNode<T> parent) { super(value); this.size = size; this.parent = parent; } @Override public int incSize() { return ++size; } @Override public int decSize() { return --size; } @Override public void incSizeOfParents() { RandomBSTNode<T> pn = parent; while (pn != null) { pn.incSize(); pn = pn.parent; } } @Override public void decSizeOfParents() { RandomBSTNode<T> pn = parent; while (pn != null) { pn.decSize(); pn = pn.parent; } } @Override public boolean isRoot() { return parent == null; } @Override public RandomBSTNode<T> getLeft() { return (RandomBSTNode<T>) left; } @Override public RandomBSTNode<T> getRight() { return (RandomBSTNode<T>) right; } @Override public int getSize() { return size; } @Override public RandomBSTNode getParent() { return parent; } @Override protected void appendMoreToString(StringBuilder sb) { sb.append(", size = ").append(size); // size, sb.append(", parent value = ").append(parent == null ? null : parent.getValue()); // parent, } /*------ unsupported methods ------*/ @Override void setLeft(BSTNode<T> left) { throw new UnsupportedOperationException("Should not set subtree directly, because size is difficult to maintain."); } @Override void setRight(BSTNode<T> right) { throw new UnsupportedOperationException("Should not set subtree directly, because size is difficult to maintain."); } } }

TestNG

(所有测试用例都会通过。)

测试方法import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Consumer; /** * RandomBST test. * * @author eric * @date 2/10/19 5:50 PM */ public class RandomBSTTest { private int n = 100; private int avgRound = (1 << 18); // avg round for each node, when get random value, private RandomBST<Integer> mbst; // minimal BST, private RandomBST<Integer> ebst; // empty BST, @BeforeMethod public void init() { // init minimal BST, mbst = CreateMinimalBST.createRandomFromNumRange(0, n); // init empty BST, ebst = new RandomBST<>(); } // other non-random related test cases are removed, // randomValue() @Test public void testRandomValue() { int round = n * avgRound; // total round, System.out.printf("tree size: %d, total round: %d, result:\n", n, round); Map<Integer, Integer> freqMap = new LinkedHashMap<>(); // init map, for (int i = 0; i < n; i++) { freqMap.put(i, 0); } // get random value & count frequency, for (int i = 0; i < round; i++) { int rv = mbst.randomValue(); freqMap.put(rv, freqMap.get(rv) + 1); } double avgPercentage = 100 / n; // average percentage, // print result, for (int x : freqMap.keySet()) { int freq = freqMap.get(x); double percentage = freq * 100.0 / round; double diffPercentage = percentage - avgPercentage; System.out.printf("\trandom value: %2d, chosen count: %d, percentage: %.4f %%, differ: %.4f %%\n", x, freq, percentage, diffPercentage); Assert.assertTrue(Math.abs((freq - avgRound) * 1.0 / avgRound) <= 0.01); // differ <= 0.01, means <= 1 / 100, } // empty tree, Assert.assertNull(ebst.randomValue()); } } 的输出:

tree size: 100, total round: 26214400, result:
    random value:  0, chosen count: 262162, percentage: 1.0001 %, differ: 0.0001 %
    random value:  1, chosen count: 262166, percentage: 1.0001 %, differ: 0.0001 %
    random value:  2, chosen count: 262897, percentage: 1.0029 %, differ: 0.0029 %
    random value:  3, chosen count: 261887, percentage: 0.9990 %, differ: -0.0010 %
    random value:  4, chosen count: 262428, percentage: 1.0011 %, differ: 0.0011 %
    random value:  5, chosen count: 262094, percentage: 0.9998 %, differ: -0.0002 %
    random value:  6, chosen count: 261662, percentage: 0.9982 %, differ: -0.0018 %
    random value:  7, chosen count: 262034, percentage: 0.9996 %, differ: -0.0004 %
    random value:  8, chosen count: 261218, percentage: 0.9965 %, differ: -0.0035 %
    random value:  9, chosen count: 262054, percentage: 0.9997 %, differ: -0.0003 %
    random value: 10, chosen count: 261821, percentage: 0.9988 %, differ: -0.0012 %
    random value: 11, chosen count: 262401, percentage: 1.0010 %, differ: 0.0010 %
    random value: 12, chosen count: 262056, percentage: 0.9997 %, differ: -0.0003 %
    random value: 13, chosen count: 262119, percentage: 0.9999 %, differ: -0.0001 %
    random value: 14, chosen count: 261878, percentage: 0.9990 %, differ: -0.0010 %
    random value: 15, chosen count: 262191, percentage: 1.0002 %, differ: 0.0002 %
    random value: 16, chosen count: 261224, percentage: 0.9965 %, differ: -0.0035 %
    random value: 17, chosen count: 262471, percentage: 1.0012 %, differ: 0.0012 %
    random value: 18, chosen count: 261906, percentage: 0.9991 %, differ: -0.0009 %
    random value: 19, chosen count: 262752, percentage: 1.0023 %, differ: 0.0023 %
    random value: 20, chosen count: 262762, percentage: 1.0024 %, differ: 0.0024 %
    random value: 21, chosen count: 262245, percentage: 1.0004 %, differ: 0.0004 %
    random value: 22, chosen count: 261942, percentage: 0.9992 %, differ: -0.0008 %
    random value: 23, chosen count: 262339, percentage: 1.0007 %, differ: 0.0007 %
    random value: 24, chosen count: 262266, percentage: 1.0005 %, differ: 0.0005 %
    random value: 25, chosen count: 261584, percentage: 0.9979 %, differ: -0.0021 %
    random value: 26, chosen count: 263036, percentage: 1.0034 %, differ: 0.0034 %
    random value: 27, chosen count: 261412, percentage: 0.9972 %, differ: -0.0028 %
    random value: 28, chosen count: 262305, percentage: 1.0006 %, differ: 0.0006 %
    random value: 29, chosen count: 261236, percentage: 0.9965 %, differ: -0.0035 %
    random value: 30, chosen count: 261681, percentage: 0.9982 %, differ: -0.0018 %
    random value: 31, chosen count: 262829, percentage: 1.0026 %, differ: 0.0026 %
    random value: 32, chosen count: 262411, percentage: 1.0010 %, differ: 0.0010 %
    random value: 33, chosen count: 263766, percentage: 1.0062 %, differ: 0.0062 %
    random value: 34, chosen count: 262448, percentage: 1.0012 %, differ: 0.0012 %
    random value: 35, chosen count: 262074, percentage: 0.9997 %, differ: -0.0003 %
    random value: 36, chosen count: 261986, percentage: 0.9994 %, differ: -0.0006 %
    random value: 37, chosen count: 261971, percentage: 0.9993 %, differ: -0.0007 %
    random value: 38, chosen count: 262403, percentage: 1.0010 %, differ: 0.0010 %
    random value: 39, chosen count: 261510, percentage: 0.9976 %, differ: -0.0024 %
    random value: 40, chosen count: 262020, percentage: 0.9995 %, differ: -0.0005 %
    random value: 41, chosen count: 262410, percentage: 1.0010 %, differ: 0.0010 %
    random value: 42, chosen count: 261910, percentage: 0.9991 %, differ: -0.0009 %
    random value: 43, chosen count: 262240, percentage: 1.0004 %, differ: 0.0004 %
    random value: 44, chosen count: 261923, percentage: 0.9992 %, differ: -0.0008 %
    random value: 45, chosen count: 261856, percentage: 0.9989 %, differ: -0.0011 %
    random value: 46, chosen count: 262708, percentage: 1.0022 %, differ: 0.0022 %
    random value: 47, chosen count: 262695, percentage: 1.0021 %, differ: 0.0021 %
    random value: 48, chosen count: 262882, percentage: 1.0028 %, differ: 0.0028 %
    random value: 49, chosen count: 262596, percentage: 1.0017 %, differ: 0.0017 %
    random value: 50, chosen count: 262872, percentage: 1.0028 %, differ: 0.0028 %
    random value: 51, chosen count: 262343, percentage: 1.0008 %, differ: 0.0008 %
    random value: 52, chosen count: 261969, percentage: 0.9993 %, differ: -0.0007 %
    random value: 53, chosen count: 261301, percentage: 0.9968 %, differ: -0.0032 %
    random value: 54, chosen count: 262206, percentage: 1.0002 %, differ: 0.0002 %
    random value: 55, chosen count: 261923, percentage: 0.9992 %, differ: -0.0008 %
    random value: 56, chosen count: 262577, percentage: 1.0017 %, differ: 0.0017 %
    random value: 57, chosen count: 262308, percentage: 1.0006 %, differ: 0.0006 %
    random value: 58, chosen count: 262600, percentage: 1.0017 %, differ: 0.0017 %
    random value: 59, chosen count: 261682, percentage: 0.9982 %, differ: -0.0018 %
    random value: 60, chosen count: 261983, percentage: 0.9994 %, differ: -0.0006 %
    random value: 61, chosen count: 262450, percentage: 1.0012 %, differ: 0.0012 %
    random value: 62, chosen count: 263582, percentage: 1.0055 %, differ: 0.0055 %
    random value: 63, chosen count: 261755, percentage: 0.9985 %, differ: -0.0015 %
    random value: 64, chosen count: 262773, percentage: 1.0024 %, differ: 0.0024 %
    random value: 65, chosen count: 261581, percentage: 0.9979 %, differ: -0.0021 %
    random value: 66, chosen count: 262071, percentage: 0.9997 %, differ: -0.0003 %
    random value: 67, chosen count: 262053, percentage: 0.9997 %, differ: -0.0003 %
    random value: 68, chosen count: 261491, percentage: 0.9975 %, differ: -0.0025 %
    random value: 69, chosen count: 261840, percentage: 0.9988 %, differ: -0.0012 %
    random value: 70, chosen count: 261837, percentage: 0.9988 %, differ: -0.0012 %
    random value: 71, chosen count: 262293, percentage: 1.0006 %, differ: 0.0006 %
    random value: 72, chosen count: 261928, percentage: 0.9992 %, differ: -0.0008 %
    random value: 73, chosen count: 260978, percentage: 0.9956 %, differ: -0.0044 %
    random value: 74, chosen count: 262260, percentage: 1.0004 %, differ: 0.0004 %
    random value: 75, chosen count: 262151, percentage: 1.0000 %, differ: 0.0000 %
    random value: 76, chosen count: 262981, percentage: 1.0032 %, differ: 0.0032 %
    random value: 77, chosen count: 261602, percentage: 0.9979 %, differ: -0.0021 %
    random value: 78, chosen count: 261938, percentage: 0.9992 %, differ: -0.0008 %
    random value: 79, chosen count: 262335, percentage: 1.0007 %, differ: 0.0007 %
    random value: 80, chosen count: 262213, percentage: 1.0003 %, differ: 0.0003 %
    random value: 81, chosen count: 262336, percentage: 1.0007 %, differ: 0.0007 %
    random value: 82, chosen count: 262356, percentage: 1.0008 %, differ: 0.0008 %
    random value: 83, chosen count: 261782, percentage: 0.9986 %, differ: -0.0014 %
    random value: 84, chosen count: 262610, percentage: 1.0018 %, differ: 0.0018 %
    random value: 85, chosen count: 261772, percentage: 0.9986 %, differ: -0.0014 %
    random value: 86, chosen count: 261874, percentage: 0.9990 %, differ: -0.0010 %
    random value: 87, chosen count: 262319, percentage: 1.0007 %, differ: 0.0007 %
    random value: 88, chosen count: 262144, percentage: 1.0000 %, differ: 0.0000 %
    random value: 89, chosen count: 261808, percentage: 0.9987 %, differ: -0.0013 %
    random value: 90, chosen count: 261980, percentage: 0.9994 %, differ: -0.0006 %
    random value: 91, chosen count: 261742, percentage: 0.9985 %, differ: -0.0015 %
    random value: 92, chosen count: 261994, percentage: 0.9994 %, differ: -0.0006 %
    random value: 93, chosen count: 262365, percentage: 1.0008 %, differ: 0.0008 %
    random value: 94, chosen count: 261954, percentage: 0.9993 %, differ: -0.0007 %
    random value: 95, chosen count: 261660, percentage: 0.9982 %, differ: -0.0018 %
    random value: 96, chosen count: 263220, percentage: 1.0041 %, differ: 0.0041 %
    random value: 97, chosen count: 261049, percentage: 0.9958 %, differ: -0.0042 %
    random value: 98, chosen count: 262000, percentage: 0.9995 %, differ: -0.0005 %
    random value: 99, chosen count: 262692, percentage: 1.0021 %, differ: 0.0021 %

===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

相关的类和界面:

  • testRandomValue()是一个简单的二叉树实现。
  • BSTBSTNode的节点隐含,包含字段:BST / value / left

  • rightRandomBSTDesign的界面。

  • RandomBSTRandomBSTDesign$RandomBSTNodeDesign的界面。

  • RandomBST$RandomBSTNode是随机的隐含实现,扩展了RandomBST,主要覆盖了add()/ delete()方法以维护BST / parent字段。 / p>

  • sizeRandomBST$RandomBSTNode的节点隐含,扩展RandomBST并添加字段:BSTNode / parent

  • size是一个工具,可帮助构建最小高度的二进制搜索树。