【 每天学习一点算法 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 分钟前
【OpenClaw】通过 Nanobot 源码学习架构---(5)Context
人工智能·算法·机器学习
diablobaal29 分钟前
云计算学习100天-第102天-Azure入门4
学习·云计算·azure
Liudef0638 分钟前
后量子密码学(PQC)深度解析:算法原理、标准进展与软件开发行业的影响
算法·密码学·量子计算
AI_零食43 分钟前
Flutter 框架跨平台鸿蒙开发 - 自定义式按钮设计应用
学习·flutter·ui·华为·harmonyos·鸿蒙
小陈phd1 小时前
多模态大模型学习笔记(三十)—— 基于YOLO26 Pose实现车牌检测
笔记·学习
野指针YZZ1 小时前
XV6操作系统:trap机制学习笔记
笔记·学习
diygwcom2 小时前
学习开源数据采集与监视控制SCADA-即工业组态开源框架FUXA
学习·开源
OYpBNTQXi2 小时前
SEAL全同态加密CKKS方案入门详解
算法·机器学习·同态加密
zl_dfq2 小时前
Python学习5 之【字符串】
python·学习
蚂蚁数据AntData2 小时前
破解AI“机器味“困境:HeartBench评测实践详解
大数据·人工智能·算法·机器学习·语言模型·开源