Генерация равномерно случайных любопытных двоичных деревьев

двоичное дерево из N узлов "любопытно", если это двоичное дерево, значения узлов которого равны 1, 2,..N и, которые удовлетворяют свойству, что

  • каждый внутренний узел дерева имеет ровно один потомок, который больше его.
  • каждое число 1,2, ..., N появляется в дереве ровно один раз.

пример любопытного двоичного дерева

  4
 / 
5   2
   / 
  1   3

можете ли вы дать алгоритм для генерации равномерно случайного любопытного двоичного дерева n узлы, которые работают в o (n) гарантированное время?

предположим, что у вас есть доступ только к генератору случайных чисел, который может дать вам (равномерно распределенное) случайное число в диапазоне [1, k] для любого 1

решение O(nlogn) time также получит мой upvote.

пожалуйста, следуйте обычному определению помеченных двоичных деревьев, чтобы рассмотреть различные любопытные двоичные деревья.

2 ответов


Ага, я думаю, что у меня есть, как создать случайную кучу в O(N) времени. (после чего используйте подход в ответе Грега Куперберга для преобразования в "любопытное" двоичное дерево.)

Изменить 2: грубый псевдокод для создания случайной мин-кучи напрямую. Max-heap идентичен, за исключением значений, вставленных в кучу, в обратном числовом порядке.

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);
}

существует биекция между" любопытными " бинарными деревьями и стандартными кучами. А именно, учитывая кучу, рекурсивно (начиная с вершины) замените каждый внутренний узел на его самый большой дочерний. И, как я узнал в StackOverflow не так давно, куча эквивалентна перестановке 1,2..., N. таким образом, вы должны сделать случайную перестановку и превратить ее в кучу; или рекурсивно сделать кучу так же, как вы сделали бы случайную перестановку. После этого вы можете преобразовать кучи к "странное дерево".