【 每天学习一点算法 2026/03/11】从前序与中序遍历序列构造二叉树

每天学习一点算法 2026/03/11

题目:从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

我们知道先序遍历的遍历顺序是根节点→左子节点→右子节点,中序遍历的遍历顺序是左子节点→根节点→右子节点。

我们以 preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]为例

  • 首先可以得到 preorder 的第一项 3 是总的根节点

  • 然后我们在 inorder 中找到 3, 那么 3 左边的部分[9]就是左子树,3 右边的部分[15,20,7]就是右子树

  • 因为左子树有内容,我们将 preorder 的第二项 9 就是 3 节点的左子节点

    复制代码
       3
     /
    9
  • 然后 9 作为根节点,[9]中找到 9,左右都没有元素了,所以他就是叶子结点了

  • 然后 preorder 第三项 20 就是 3 的右节点

    复制代码
       3
     /   \
    9    20
  • 然后 20 作为根节点,[15,20,7]中找到 20,分为左边部分 [15] 和 右边部分 [7],后面也就按这个步骤一直分割就行,这样就可以得到树结构

    复制代码
       3
     /   \
    9    20
       /    \
      15     7

我们可以看到,我们需要做的是,遍历先序的数组,然后不断分割中序数组,最容易想到的就是递归

typescript 复制代码
/**
 * Definition for a binary tree node.
 * class TreeNode {
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     }
 * }
 */

function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
  let index = 0 // preorder 指针
  return helper(inorder) // 调用辅助函数递归生成树
  function helper(arr: number[]) {
    if (arr.length === 0) return null // 分割结果为空是返回null
    const node = new TreeNode(preorder[index]) // 根据当前指针创建当前根节点
    const i = arr.findIndex(val => preorder[index] === val) // 找到根节点在 arr 中位置
    if (i > 0) {
      // 存在左树数据
      index++
      node.left = helper(arr.slice(0, i))
    }
    if (i < arr.length - 1) {
      // 存在右树数据
      index++
      node.right = helper(arr.slice(i + 1))
    }
    return node // 返回当前根节点
  }
}; 

还有一个迭代的方法:

有点复杂我们直接用一个例子来讲:

复制代码
        3
       / \
      9  20
     /  /  \
    8  15   7
   / \
  5  10
 /
4

preorder = [3, 9, 8, 5, 4, 10, 20, 15, 7]
inorder = [4, 5, 8, 10, 9, 3, 15, 20, 7]

我们观察一下 preorder[i]preorder[i + 1],有两种情况

  • preorder[i + 1]preorder[i] 的左子结点
  • preorder[i] 没有左子节点, preorder[i + 1]preorder[i] 某一个父节点的 右子节点

根据这个规律我们可以维护一个栈存放判断右节点的节点,通过遍历 preorder 来还原树结构,具体步骤如下:

  • 首先定义一个指针指向 inorder 的第一个元素 4

  • 接着我们开始遍历 preorder ,首先是 3,首位直接入栈

  • 然后遍历到 9,关键点来了,我们如何判断这个 9 是不是 3 的左节点呢?假设不是的话,3 是不是就应该是最左侧的节点,但是我们目前还没做任何操作,最左侧的节点应该是 4,所以我们可以确定 93 的左子结点,我们将 3 左侧指向节点 9,并将节点 9入栈。

  • 然后 8, 5, 4一样的操作,等到 10 时,我们可以发现 上一个节点 4 就是当前最左侧的节点,那么 10 就是节点 4的某个父节点

  • 我们怎么寻找这个父节点呢?我们可以看到,只有左节点的树前序遍历的结果就是中序遍历结果的反转,那么移动的指针,并弹栈,弹栈节点和指针指向值相等时,就是我们要找的父节点 8 ,我们将8 右侧指向节点 9,并将节点 9入栈。

  • 就这样遍历完所有的 preorder 元素就可以还原整个树了

    function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
    if (preorder.length === 0) return null
    const stack: TreeNode[] = []
    const root = new TreeNode(preorder[0])
    stack.push(root)
    let index = 0
    for (let i = 1; i < preorder.length; i++) {
    let node = stack[stack.length - 1]
    if (inorder[index] !== node.val) {
    node.left = new TreeNode(preorder[i])
    stack.push(node.left)
    } else {
    while (stack.length > 0 && stack[stack.length - 1].val === inorder[index]) {
    index++
    node = stack.pop()
    }
    node.right = new TreeNode(preorder[i])
    stack.push(node.right)
    }

    复制代码
    }
    return root

    };

题目来源:力扣(LeetCode)

相关推荐
进击切图仔2 小时前
ROS 行为(Action)机制
算法
飞Link2 小时前
概率图模型的基石:隐马可夫模型 (HMM) 深度解析
开发语言·python·算法
_日拱一卒2 小时前
LeetCode(力扣):验证回文串
算法·leetcode·职场和发展
Eward-an2 小时前
LeetCode 128. 最长连续序列(O(n)时间复杂度详解)
数据结构·算法·leetcode
敲代码的嘎仔2 小时前
Java后端开发——基础面试题汇总
java·开发语言·笔记·后端·学习·spring·中间件
Frostnova丶2 小时前
LeetCode 1009 & 476 数字的补数
算法·leetcode
CppBlock2 小时前
HPX vs TBB vs OpenMP:并行任务模型对比
c++·算法
17(无规则自律)2 小时前
Leetcode第六题:用 C++ 解决三数之和
c++·算法·leetcode