Как преобразовать список в двоичное дерево без рекурсии - PullRequest
1 голос
/ 11 марта 2019

В качестве упражнения я пытаюсь преобразовать список в двоичное дерево без использования рекурсии.

Это в основном то, где я застреваю.

var items = [
  { a: 1 }, { a: 10 }, { a: 100 }, { a: 20 }, { a: 2 },
  { a: 3 }, { a: 30 }, { a: 300 }, { a: 40 }, { a: 4 }
]

var top = {}

for (var i = 0, n = items.length; i < n; i+=2) {
  var a = items[i]
  var b = items[i + 1]

  top.left = { data: a }
  top.right = { data: b }

  top = top.right
}

Очевидно, что top = top.rightне правильно, и это делает так, что создается только одна ветвь расширяющегося дерева, так что в основном это просто связанный список с маленьким бутоном листа на каждом уровне.

    /\
     /\
      /\
       /\
        /\
          \

У меня трудностипонимание того, как пройти вниз все ветви дерева и , расположить узлы в порядке в двоичном дереве, где их можно перебирать / проходить в исходном порядке, в котором они были в списке,Я не уверен, как это называется, если я должен делать какую-то версию DFS / BFS inorder / preorder / postorder, я не слишком уверен.Интересно, можно ли продемонстрировать в JS, как это можно сделать?

Я не уверен, каким должен быть даже результат, но если бы мне пришлось угадывать, это было бы так:

             top
       1             10
  100     20      2      3
30  300  40 4

Ответы [ 4 ]

2 голосов
/ 11 марта 2019

Вы можете использовать два индекса: один для списка узел , а другой для списка target .Затем поместите следующие два узла слева и справа от узла target .

Продолжайте до тех пор, пока больше не будет доступен узел.

В этом дереве нет пустого начального узла.

var items = [{ a: 1 }, { a: 10 }, { a: 100 }, { a: 20 }, { a: 2 }, { a: 3 }, { a: 30 }, { a: 300 }, { a: 40 }, { a: 4 }],
    tree = items[0],
    i = 1,
    j = 0;

while (i < items.length) {
    items[j].left = items[i++];
    if (i >= items.length) break;
    items[j].right = items[i++];
    j++;
}

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; } 

Немного сокращенный подход.

var items = [{ a: 1 }, { a: 10 }, { a: 100 }, { a: 20 }, { a: 2 }, { a: 3 }, { a: 30 }, { a: 300 }, { a: 40 }, { a: 4 }],
    tree = items[0],
    i = 1,
    side = 'right';

while (i < items.length) {
    side = { left: 'right', right: 'left' }[side];
    items[(i - 1) >> 1][side] = items[i++];
}

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
1 голос
/ 11 марта 2019

Первое, что нам нужно решить, это структура нашего дерева. Вместо того, чтобы иметь отдельный корневой узел, было бы целесообразно, чтобы первый элемент списка был корневым. Следовательно, наша древовидная структура данных может быть определена следующим образом:

// A Tree(a) is one of:
// - null
// - tree(a, Tree(a), Tree(a))
const tree = (value, left, right) => ({ value, left, right });

Следовательно, наше дерево вывода будет выглядеть так:

                                 1
                                 |
                    +------------+------------+
                    |                         |
                    10                       100
                    |                         |
          +---------+---------+         +-----+-----+
          |                   |         |           |
          20                  2         3           30
          |                   |         |           |
    +-----+-----+           +-+-+    +--+--+     +--+--+
    |           |           |   |    |     |     |     |
   300          40          4  null null  null  null  null
    |           |           |
 +--+--+     +--+--+     +--+--+
 |     |     |     |     |     |
null  null  null  null  null  null

Если бы мы использовали рекурсию, то код прост, потому что деревья - это рекурсивные структуры данных:

const buildTree = (xs, i = 0) => i >= xs.length ? null :
    tree(xs[i], buildTree(xs, 2 * i + 1), buildTree(xs, 2 * i + 2));

Собираем все вместе:

// A Tree(a) is one of:
// - null
// - tree(a, Tree(a), Tree(a))
const tree = (value, left, right) => ({ value, left, right });    

const buildTree = (xs, i = 0) => i >= xs.length ? null :
    tree(xs[i], buildTree(xs, 2 * i + 1), buildTree(xs, 2 * i + 2));

console.log(buildTree([1, 10, 100, 20, 2, 3, 30, 300, 40, 4]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

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


Вот алгоритмический метод преобразования любой рекурсивной функции в итерационную функцию. Сначала преобразуйте функцию в стиль продолжения:

const buildTree = (xs, i = 0, k = x => x) => i >= xs.length ? k(null) :
    buildTree(xs, 2 * i + 1, left =>
    buildTree(xs, 2 * i + 2, right =>
    k(tree(xs[i], left, right))));

Затем замените продолжения структурой данных, которая содержит все необходимые свободные переменные продолжения. Также замените вызовы продолжения приложением к функции applyCont, которая содержит логику продолжения:

const buildTree = (xs, i, k = null) => i >= xs.length ? applyCont(k, null) :
    buildTree(xs, 2 * i + 1, { xs, i, k });

const applyCont = (k, x) => k === null ? x :
    !k.hasOwnProperty("x") ? buildTree(k.xs, 2 * k.i + 2, { x, xs: k.xs, i: k.i, k: k.k }) :
    applyCont(k.k, tree(k.xs[k.i], k.x, x));

Если вы заметили, структура продолжений теперь похожа на связанный список. Фактически, вы можете думать о продолжениях как о стеке кадров (то есть о программном стеке). Теперь этот рекурсивный код легко преобразовать в итеративный код следующим образом:

const buildTree = xs => {
    var i = 0, stack = null;

    loop: do {
        while (i < xs.length) {
            i = 2 * i + 1;
            k = { i, k };
        }

        var x = null;

        while (k) {
            if (!k.hasOwnProperty("x")) {
                i = 2 * k.i + 2;
                k.x = x;
                continue loop;
            }

            k = k.k;
            x = tree(k.xs[k.i], k.x, x);
        }

        return x;
    } while (true);
};

Надеюсь, это поможет.

1 голос
/ 11 марта 2019

Хорошо, код ниже.Это сгенерирует двоичное дерево как OP, упомянутый из данного items.Функция будет использовать while и for loop

Объяснение:

Вся идея состоит в том, чтобы отслеживать объекты, которые мы хотим вставить новые значения в массив.
Давайте рассмотрим, что у нас есть массив элементов вроде этого

var items = [
  { a: 1 }, { a: 10 }, { a: 100 }, { a: 20 }, { a: 2 },
  { a: 3 }, { a: 30 }, { a: 300 }, { a: 40 }, { a: 4 }
]

Прежде всего, мы создаем пустой объект.obj = {}.На данный момент нам нужно добавить .left и .right obj.Поэтому мы изначально установили next = [obj].
. В for .left и right будут добавлены к obj, так как next содержит только один элемент. Таким образом, цикл Object выглядит следующим образом.

{
    left:{ a: 1 }, 
    right:{ a: 10 }
}

Теперь при следующей остановке мы хотим left и right обеим клавишам текущего Объекта.Таким образом, мы выдвигаем оба к next массиву.Так что следующий массив выглядит

[{},{ a: 1 },{ a: 10 }]

Обратите внимание, что теперь i = 1 Так что в следующем for цикл left и right добавляются к обоим последним элементам массива. Как элементы в массиве ссылаютсясвойства объекта.Так что это будет мутировать объект.obj

{
    left:{ a: 1,left:{ a: 100 },right:{ a: 20 }},
    right:{ a: 10, right:{ a: 2 },left:{ a: 3 }}
}

И теперь next будет

[{},{ a: 1 },{ a: 10 },{ a: 100 },{ a: 20 },{ a: 2 },{ a: 3 }]

Теперь в следующем цикле следующие оставшиеся 4 элементы будут добавлены к потомкамleft и right из { a: 100 } и { a: 20 }items.length станет 0 и return obj.

var items = [
  { a: 1 }, { a: 10 }, { a: 100 }, { a: 20 }, { a: 2 },
  { a: 3 }, { a: 30 }, { a: 300 }, { a: 40 }, { a: 4 }
]

function bTree(items){
let obj = {}
let next = [obj];
let i = 0;
while(items.length){
   for(i;i<next.length;i++){      
        next[i].left = items[0]
        next[i].right = items[1]
        items.splice(0,2)
      
        next.push(next[i].left)
        next.push(next[i].right);
        if(!items.length) return obj;
      }
   
   }
}
console.log(bTree(items))
0 голосов
/ 11 марта 2019

@ AaditMShah вот грубая итеративная на основе того, что вы отправили. Спасибо.

var items = [ 1, 10, 100, 20, 2, 3, 30, 300, 40, 4 ]
var tree = {}
var node = tree
var stack = [ node ]
var i = 0

while (stack.length) {
  var node = stack.shift()
  node.value = items[i]

  if (node.value) {
    node.left = {}
    stack.push(node.left)
    node.right = {}
    stack.push(node.right)
  }

  i++
}

console.log(JSON.stringify(tree, null, 2))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...