返回

剑指 Offer II 070. 从前序与中序遍历序列构造二叉树

前端

如何根据前序和中序遍历序列构造二叉树

在计算机科学领域,二叉树 是一种重要的数据结构,它由一个根节点和两个子树(左子树和右子树)组成。前序遍历中序遍历 是遍历二叉树的两种常见方法。

前序遍历从根节点开始,依次访问每个节点及其左子树和右子树。而中序遍历从根节点的左子树开始,依次访问每个节点,然后访问根节点,最后访问根节点的右子树。

给定前序和中序遍历序列,我们如何构造对应的二叉树呢?别担心,让我们一步步来分解这个过程。

算法步骤

  1. 确定根节点: 从前序遍历序列中取出第一个元素,它就是二叉树的根节点。

  2. 查找根节点在中序遍历序列中的位置: 在中序遍历序列中找到根节点的值,它的位置将把序列分成两个部分:左子树和右子树。

  3. 递归构造子树: 使用相同的方法,对左子树和右子树分别进行前序和中序遍历。对于左子树,使用前序遍历序列的第二个元素到根节点位置之间的元素,以及中序遍历序列根节点左侧的部分。对于右子树,使用前序遍历序列根节点位置之后的元素,以及中序遍历序列根节点右侧的部分。

代码示例

// 创建二叉树节点
class TreeNode {
  constructor(val) {
    this.val = val;
    this.left = null;
    this.right = null;
  }
}

/**
 * 根据前序和中序遍历序列构造二叉树
 * @param {number[]} preorder 前序遍历序列
 * @param {number[]} inorder 中序遍历序列
 * @returns {TreeNode} 二叉树的根节点
 */
const buildTree = (preorder, inorder) => {
  // 创建哈希表存储中序遍历序列元素的位置
  const inorderMap = new Map();
  for (let i = 0; i < inorder.length; i++) {
    inorderMap.set(inorder[i], i);
  }

  // 递归函数构造二叉树
  const buildTreeHelper = (preStart, preEnd, inStart, inEnd) => {
    // 前序遍历序列范围为空,返回 null
    if (preStart > preEnd) {
      return null;
    }

    // 取前序遍历序列的第一个元素作为根节点值
    const rootVal = preorder[preStart];

    // 在中序遍历序列中查找根节点位置
    const rootIndex = inorderMap.get(rootVal);

    // 创建根节点
    const root = new TreeNode(rootVal);

    // 递归构造左子树
    root.left = buildTreeHelper(
      preStart + 1,
      preStart + rootIndex - inStart,
      inStart,
      rootIndex - 1
    );

    // 递归构造右子树
    root.right = buildTreeHelper(
      preStart + rootIndex - inStart + 1,
      preEnd,
      rootIndex + 1,
      inEnd
    );

    // 返回根节点
    return root;
  };

  // 调用递归函数构造二叉树
  return buildTreeHelper(0, preorder.length - 1, 0, inorder.length - 1);
};

动画演示

为了更直观地理解这一过程,这里有一个动画演示:

[动画演示链接]

常见问题解答

1. 如何确定根节点?

根节点是前序遍历序列的第一个元素。

2. 如何找到根节点在中序遍历序列中的位置?

在中序遍历序列中,根节点的值将序列分成左右两部分。根节点位于这两部分的连接处。

3. 为什么需要哈希表?

哈希表用于快速查找中序遍历序列中每个元素的位置。这可以大大提高算法的效率。

4. 算法的复杂度是多少?

算法的时间复杂度为 O(n),其中 n 是二叉树中的节点数。

5. 有没有其他构造二叉树的方法?

除了前序和中序遍历序列,还可以使用其他遍历顺序,例如后续遍历和层序遍历来构造二叉树。