二叉树前序中序后序遍历

二叉树的遍历,详细概念还是看搜索引擎总结吧,二叉树遍历

二叉树,前序、中序、后序,遍历,都是深度优先遍历
前中后指的是根节点的访问顺序
对最底层的节点而言,前中后就是三个节点
对于非最底层的节点而言,左右两个节点就是分支,不仅仅是三个节点

const tree = {
  value: 1,
  left: {
    value: 2,
    left: {
      value: 4,
      left: {
        value: 8,
      },
      right: {
        value: 9,
      },
    },
    right: {
      value: 5,
      left: {
        value: 10,
      },
      right: {
        value: 11,
      },
    },
  },
  right: {
    value: 3,
    left: {
      value: 6,
      left: {
        value: 12,
      },
      right: {
        value: 13,
      },
    },
    right: {
      value: 7,
      left: {
        value: 14,
      },
      right: {
        value: 15,
      },
    },
  },
};

// 前序遍历
// 前序遍历:首先访问根结点,然后遍历左子树,最后遍历右子树(根->左->右)
// 顺序:访问根节点->前序遍历左子树->前序遍历右子树

// 递归解法
function preorderTraversal1(tree) {
  const arr = [];
  function recursion(node) {
    if (!node) return;
    arr.push(node.value);
    recursion(node.left);
    recursion(node.right);
  }
  recursion(tree);
  return arr;
}
// 栈迭代解法
function preorderTraversal2(tree) {
  const arr = [];
  const stack = [tree];
  while(stack.length) {
    const node = stack.pop();
    if (node) {
      arr.push(node.value);
      // 注意这里由于先进后出,所以右节点先进入
      stack.push(node.right);
      // 注意这里由于先进后出,看起来是根右左,其实是根左右
      stack.push(node.left);
    }
  }
  return arr;
}

// 中序遍历
// 中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树(左->根->右)
// 顺序:中序遍历左子树->访问根节点->中序遍历右子树

// 递归解法
function inorderTraversal1(tree) {
  const arr = [];
  function recursion(node) {
    if (!node) return;
    recursion(node.left);
    arr.push(node.value);
    recursion(node.right);
  }
  recursion(tree);
  return arr;
}
// 栈迭代解法
function inorderTraversal2(node) {
  const arr = [];
  const stack = [];
  while(stack.length || node) {
    // 第一次大循环进来 node 是 1
    // 第二次大循环进来 node 是 undefined
    // 第三次大循环进来 node 是 9
    // 第四次大循环进来 node 是undefined
    while(node) {
      // 第一次小循环后,[1,2,4,8]
      // 第二次没有小循环
      // 第三次小循环后,[1,2,9]
      // 第四次没有小循环
      stack.push(node);
      node = node.left;
    }
    // 第一次大循环访问 8 这个节点
    // 第二次大循环访问 4 这个节点,注意这里的 4 是根节点,接下来要访问它的右分支
    // 第三次大循环访问 9 这个节点
    // 第四次大循环访问 2 这个节点,注意这里的 2 是根节点,接下来要访问它的右分支
    node = stack.pop();
    arr.push(node.value);
    // 第一次大循环的 8 没有子节点,node 为 undefined
    // 第二次大循环的 4 right 节点是 
    /* right: {
      value: 9,
    } */
    // 第三次大循环的 9 没有子节点,node 为 undefined
    // 第四次大循环的 2 right 节点是
    /* {
      value: 5,
      left: {
        value: 10,
      },
      right: {
        value: 11,
      },
    } */
    // 然后每个小节点就又如上述描述的过程,根据“左根右”顺序遍历
    node = node.right;
  }
  return arr;
}

// 后序遍历
// 后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点(左->右->根)
// 顺序:后序遍历左子树->后序遍历右子树->访问根节点

// 递归解法
function postorderTraversal1(tree) {
  const arr = [];
  function recursion(node) {
    if (!node) return;
    recursion(node.left);
    recursion(node.right);
    arr.push(node.value);
  }
  recursion(tree);
  return arr;
}
// 栈迭代解法
function postorderTraversal2(node) {
  const arr = [];
  const stack = [node];

  // // 可以采用前序遍历的方法,最后整个数组反转
  // while(stack.length) {
  //   node = stack.pop();
  //   if (node) {
  //     arr.push(node.value);
  //     // 注意这里由于先进后出,看起来是根左右,但其实是根右左
  //     // 在下面 reverse 之后,就是左右根,变成后序了
  //     stack.push(node.left);
  //     stack.push(node.right);
  //   }
  // }
  // return arr.reverse();

  // 可以采用前序遍历的方法,先进的放在最后,变成后序遍历了
  while(stack.length) {
    node = stack.pop();
    if (node) {
      arr.unshift(node.value);
      stack.push(node.left);
      stack.push(node.right);
    }
  }
  return arr;
}

console.log('前序遍历-递归: ', preorderTraversal1(tree));

console.log('前序遍历-迭代:', preorderTraversal2(tree));

console.log('中序遍历-递归: ', inorderTraversal1(tree));

console.log('中序遍历-迭代: ', inorderTraversal2(tree));

console.log('后序遍历-递归: ', postorderTraversal1(tree));

console.log('后序遍历-迭代: ', postorderTraversal2(tree));