【 每天学习一点算法 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,所以我们可以确定 9 是 3 的左子结点,我们将 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)

相关推荐
专注仿真5 小时前
ACM算法
算法·深度优先
差不多的周周6 小时前
《骨架上下文:基于零镜头骨架的动作识别的骨架侧上下文提示学习》论文核心部分详解
学习
Century_Dragon7 小时前
从挂图到VR:汽修专业的一堂新能源原理课可以怎么上~
学习
知识分享小能手8 小时前
C++ 学习教程,从入门到精通,C++对象和类 — 详细知识点总结(10)
开发语言·c++·学习
顺顺 尼8 小时前
linux 进程信号(上)
linux·c++·算法
罗西的思考8 小时前
[Agent Memory / 强化学习] MemPO源码学习笔记 ---(5)--- GRPO
人工智能·算法·机器学习
richard_yuu8 小时前
动态规划:强化学习的「数学基础」,从 MDP 到值迭代
深度学习·神经网络·算法·yolo·机器学习·动态规划
New农民工9 小时前
倒立摆训练秘籍:理解训练参数
学习
mmmmath_39 小时前
LeetCode.225.用队列实现栈
算法
Elaine3369 小时前
数据结构与算法-程序
数据结构·python·算法·计算机基础·编程基础