如何将列表转换为二叉树而无需递归

时间:2019-03-11 06:34:13

标签: javascript list tree

作为练习,我试图将列表转换为二叉树而不使用递归。

这基本上就是我所困的地方。

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,我不太确定。想知道是否可以在JS中演示如何做到这一点。

我不确定输出应该是什么,但是如果我不得不猜测它将是这样的:

             top
       1             10
  100     20      2      3
30  300  40 4

4 个答案:

答案 0 :(得分:2)

您可以使用两个索引,一个用于 node 列表,另一个用于 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 :(得分:1)

确定代码如下。这将生成给定items中提到的OP所描述的二叉树。该函数将使用whilefor循环

说明:

整个想法是跟踪我们要在数组中插入新值的对象。
让我们考虑我们有这样的项目数组

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

首先,我们创建一个空对象。 obj = {}。此时,我们需要在.left中添加.rightobj。因此,我们最初设置了next = [obj]
for中,.leftright将被添加到obj中,因为next仅包含一个元素。因此,对象像这样循环。

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

现在,在下一站中,我们想leftright到当前对象的两个键。因此,我们将两者都推送到next数组中。 这样下一个数组看起来

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

请注意,i = 1现在在下一个for循环中,leftright被添加到array的最后两个元素。因为array中的元素正在引用的属性。宾语。因此,这将使obj变异。 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元素将被添加到leftright的{​​{1}}和{ a: 100 }的子级中。并且{ a: 20 }将成为items.length,它将0 return

obj

答案 2 :(得分:1)

我们需要决定的第一件事是树的结构。与其拥有一个单独的根节点,不如将列表的第一个元素作为根。因此,我们的树数据结构可以定义如下:

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

希望有帮助。

答案 3 :(得分:0)

@AaditMS这里是根据您发送的内容进行的粗略迭代。谢谢。

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