关于算法:生成均匀随机好奇的二叉树

Generating uniformly random curious binary trees

如果一个n个节点的二叉树的节点值为1、2、…、n,并且满足以下属性,那么它就是"好奇的"。

  • 树的每个内部节点只有一个比它大的子代。
  • 1,2,…,n中的每一个数字都恰好出现在树中一次。

一个奇怪的二叉树的例子

1
2
3
4
5
  4
 / \
5   2
   / \
  1   3

你能给出一个算法来生成一个在O(N)保证时间内运行的n个节点的一致随机奇异二叉树吗?

假设您只能访问一个随机数生成器,对于任何1<=k<=n,该生成器都可以在[1,k]范围内给您一个(均匀分布的)随机数。假设生成器在o(1)中运行。

O(非登录)时间解决方案也会得到我的支持。

请遵循标记的二叉树的一般定义,以考虑不同的奇异二叉树。


在"好奇的"二叉树和标准堆之间有一个双射。也就是说,给定一个堆,递归地(从顶部开始)交换每个内部节点及其最大的子节点。正如我不久前在StackOverflow中了解到的,堆相当于1,2,…,n的排列,所以你应该做一个随机排列,然后把它变成一个堆;或者递归地做堆,就像你做随机排列一样。之后,您可以将堆转换为"好奇的树"。


啊哈,我想我已经知道如何在O(N)时间内创建一个随机堆了。(之后,使用GregKuperberg的答案中的方法将其转换为"好奇的"二叉树。)

编辑2:直接生成随机最小堆的粗糙伪代码。最大堆是相同的,除了插入到堆中的值是相反的数字顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
struct Node {
   Node left, right;
   Object key;
   constructor newNode() {
     N = new Node;
     N.left = N.right = null;
     N.key = null;
   }
}

function create-random-heap(RandomNumberGenerator rng, int N)
{
   Node heap = Node.newNode();
   // Creates a heap with an"incomplete" node containing a null, and having
   // both child nodes as null.

   List incompleteHeapNodes = [heap];
   // use a vector/array type list to keep track of incomplete heap nodes.

   for k = 1:N
   {
      // loop invariant: incompleteHeapNodes has k members. Order is unimportant.

     int m = rng.getRandomNumber(k);
     // create a random number between 0 and k-1
     Node node = incompleteHeapNodes.get(m);
     // pick a random node from the incomplete list,
     // make it a complete node with key k.
     // It is ok to do so since all of its parent nodes
     // have values less than k.
     node.left = Node.newNode();
     node.right = Node.newNode();
     node.key = k;

     // Now remove this node from incompleteHeapNodes
     // and add its children. (replace node with node.left,
     // append node.right)

     incompleteHeapNodes.set(m, node.left);
     incompleteHeapNodes.append(node.right);

     // All operations in this loop take O(1) time.
   }

   return prune-null-nodes(heap);
}

// get rid of all the incomplete nodes.
function prune-null-nodes(heap)
{
   if (heap == null || heap.key == null)
      return null;
   heap.left = prune-null-nodes(heap.left);
   heap.right = prune-null-nodes(heap.right);
}