用javascript生成一个二叉树节点,并且构建一个二叉树

181 阅读4分钟
function TreeNode(val) {
  this.val = val;
  this.left = null;
  this.right = null;
}

function createBinaryTree(inputList) {
  let node = null;
  if (!inputList || inputList.length === 0) {
    return null;
  }
  const data = inputList.shift();
  if (data !== null) {
    node = new TreeNode(data);
    node.left = createBinaryTree(inputList);
    node.right = createBinaryTree(inputList);
  }
  return node;
} 

const inputList = [3, 2, 9, null, null,10,null,null,8,null, 4];
const root = createBinaryTree(inputList);
console.log(root); 
/**
{
  "val": 3,
  "left": {
      "val": 2,
      "left": {
          "val": 9,
          "left": null,
          "right": null
      },
      "right": {
          "val": 10,
          "left": null,
          "right": null
      }
  },
  "right": {
      "val": 8,
      "left": null,
      "right": {
          "val": 4,
          "left": null,
          "right": null
      }
  }
}
*/

我们现在已经能成功的构建出二叉树了,接下来我们来学习一下二叉树的遍历。显示递归的方式去写,这种通过递归栈的方式实现的遍历整体而言代码简单易于理解。具体代码如下。

function preOrderTraversal(node) {
  if (node == null) {
    return;
  }
  console.log(node.val);
  preOrderTraversal(node.left);
  preOrderTraversal(node.right);
}
console.log("前序遍历")
preOrderTraversal(root)
// 3 2 9 10 8 4
function inOrderTraversal(node) {
  if (node == null) {
    return;
  }
  inOrderTraversal(node.left);
  console.log(node.val);
  inOrderTraversal(node.right);
}
console.log("中序遍历")
inOrderTraversal(root)
// 9 2 10 3 8 4
function postOrderTraversal(node) {
  if (node == null) {
    return;
  }
  postOrderTraversal(node.left);
  postOrderTraversal(node.right);
  console.log(node.val);
}
console.log("后序遍历")
postOrderTraversal(root)
// 9 10 2 4 8 3

除了用递归的方式,我们也可以利用栈来实现二叉树非递归遍历,这里用到了回溯的思想,以前序遍历举例。

// 二叉树非递归前序遍历
function preOrderTraversalWithStack(root) {
  const stack = [];
  let treeNode = root;
  while (treeNode || stack.length > 0) {
    while (treeNode) {
      console.log('打印', treeNode.val);
      stack.push(treeNode);
      // console.log('stack push', stack)
      treeNode = treeNode.left;
      // console.log('treeNode', treeNode ? treeNode.val : null)
    }
    if (stack.length > 0) {
      treeNode = stack.pop();
      // console.log('treeNode', treeNode ? treeNode.val : null)
      // console.log('stack pop', stack)
      treeNode = treeNode.right;
      // console.log('treeNode', treeNode ? treeNode.val : null)
    }
  }
}
console.log("非递归前序遍历")
preOrderTraversalWithStack(root)

这段代码实现了二叉树的前序遍历算法,使用了栈的数据结构来辅助实现。 具体来说,代码中首先定义了一个空的栈和一个指向二叉树根节点的指针treeNode,然后通过while循环来遍历整个二叉树。 在while循环中,首先使用另一个while循环来将当前节点的所有左子节点都依次入栈,并将当前节点更新为其左子节点,直到当前节点的左子节点为空为止。这一步操作实现了先序遍历算法中访问节点的操作,即先访问根节点,然后依次访问其左子节点。 当当前节点的左子节点为空时,代码会进入if语句块中。在这里,代码会将栈顶的节点出栈并将其右子节点赋值给treeNode,以便继续遍历右子树。这一步操作实现了先序遍历算法中回溯的操作,即在左子树遍历完之后回到其父节点,然后再遍历其右子节点。 在while循环中,如果栈为空并且当前节点为空,那么整个遍历过程就结束了。 总体来说,这段代码使用了栈的数据结构来辅助实现二叉树的前序遍历算法,实现了依次访问节点、回溯到父节点并继续遍历右子树的操作。

我们以[1,2,null,null,3,null,null]这样一棵树来具体说明。

下面是对这段代码在输入为[1,2,null,null,3,null,null]的二叉树上的执行过程的解释:

    1
   /  \
  2    3
 / \   / \
n   n n   n
  1. 首先,根据输入数组构造出二叉树。这个二叉树的根节点是值为1的节点,它有一个左子节点值为2,一个右子节点值为3。
  2. 然后,我们调用 preOrderTraversalWithStack 函数,并将根节点作为参数传入。此时 stack 数组为空,treeNode 指向根节点。
  3. 进入 while 循环,由于 treeNode 不为空,所以我们进入内层 while 循环。首先打印出 treeNode 的值,也就是1。然后将 treeNode 入栈,并将 treeNode 的左子节点赋值给 treeNode。
  4. treeNode 现在指向节点2。由于 treeNode 不为空,我们进入内层 while 循环。打印出 treeNode 的值,也就是2。然后将 treeNode 入栈,并将 treeNode 的左子节点赋值给 treeNode。
  5. treeNode 现在指向 null。因此,内层 while 循环结束。由于此时 stack 数组中有两个元素,我们从 stack 中弹出一个节点,并将 treeNode 指向该节点的右子节点null。这个节点是节点2。因此,此时 stack 数组中还有一个元素,它是节点1。
  6. treeNode 现在指向null, 但是stack中还有节点1,因此进入stack.length>0, 从栈里面pop出节点1,此时栈为空m=,并把treeNode指向节点1的right,也就是3
  7. treeNode 现在指向节点3。由于 treeNode 不为空,我们进入内层 while 循环。打印出 treeNode 的值,也就是3。接着将节点3入栈,此时栈的长度为1,然后将 treeNode 的左子节点赋值给 treeNode。由于节点3没有左子节点,treeNode 现在指向 null。
  8. 由于此时treeNode为null,因此跳出循环
  9. 由于 stack 数组中还有一个元素,也就是节点3,所以我们从 stack 中弹出该节点,并将 treeNode 指向该节点的右子节点null。此时 stack 数组为空。 10.由于 treeNode 现在指向 null,内层 while 循环结束。我们回到外层 while 循环的判断条件,发现 stack 数组为空且 treeNode 为 null,因此整个算法结束。