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

1.题目

这道题是2024-2-20的签到题,题目难度为中等。

考察的知识点为递归。

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

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

2.思路

推荐一本书

这道题其实很有意思,前几天我们实现的是给我们一个树结构,让我们遍历返回它的值列表;而今天的这个题是给我们一个值列表,让我们构建一棵树。其实刚开始我也是摸不清头绪,只知道方法可能与递归有关系。

于是我想起了自己当初学习数据结构的书上好像有类似的思路,这里我推荐一本学习数据结构的书------《大话数据结构》,这本书中图形讲解比较多,让我们能够更直观的理解数据结构,之后小徐也会在博客中分享一些比较实用的东西。

弄明白树的递归遍历

其实这道题还是考察我们对树的遍历理解:前序遍历、中序遍历、后序遍历。

前序遍历的遍历顺序是:根节点 ------> 左子树 ------> 右子树

中序遍历的遍历顺序是:左子树 ------> 根节点 ------> 右子树

后序遍历的遍历顺序是:左子树 ------> 右子树 ------> 根节点

因此只要我们弄清楚上面的思路逻辑顺序后无论题型怎么变化我们都能解出来。

核心思路

在前面提到的思路后,我们根据题目提供的两个列表:

对于所有的树(包括子树),它都满足下面这个情况:

前序遍历数组:**根节点值,**(左子树的所有结点值),(右子树的所有结点值)

中序遍历数组:(左子树的所有结点值),**根节点值,**(右子树的所有结点值)

所以这道题的核心思路是我们该如何找到每棵树的两个遍历数组, 换个思路,他就是让我们找到每棵树的两个遍历数组的起始点, 然后我们就可以根据这两个数组来找到这棵树的根节点、左子树(左子结点)、右子树(右子结点)。

递归思路

确定递归函数和参数

首先,我们可以定义一个递归函数,用来构建树结构,无论是大树还是小树都可以构建。同时我们还要定义这个函数的四个参数:

p_start:当前树的前序遍历开始索引

p_end:当前树的前序遍历结束索引

i_start:当前树的中序遍历开始索引

i_end:当前树的中序遍历结束索引

找到递归终止条件

然后,我们还需要确定递归函数的终止条件(总不可能无尽的套娃吧)。我们在递归子树的时候,肯定会在当前树的四个索引上进行加减,因此肯定会存在p_start大于p_end的情况或者i_start大于i_end的情况,这说明当前树已经不能继续递归了,需要停止并返回None(NuLL)。

核心构建逻辑

根据前面的介绍,当前树结点的值肯定是前序遍历数组的第一个,因此我们可以首先构建一个根节点,然后我们根据这个值来找它在中序遍历数组中的下标索引ino_root_index,然后我们可以根据当前树的中序遍历数组的i_start和i_end来确定它的左子树结点个数leftNum 和右子树结点个数rightNum。

那么接下来我们是不是可以根据当前树的4个索引和左子树结点个数、右子树结点个数来递归它的子树呢?肯定可以。

左子树参数公式推导

它的左子树结点的4个参数满足,这里我使用parent代表父母结点,son代表子结点:

右子树参数公式推导

它的右子树结点的4个参数满足,这里我使用parent代表父母结点,son代表子结点:

3.代码

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        # 数组的长度
        length = len(preorder)
        
        # 构建树的递归函数
        def buildTree(p_start,p_end,i_start,i_end):
            # 如果前序遍历起始点start超过了结束点end,则返回None,说明不能递归了
            if p_start > p_end:
                return None
            # 当前树根节点在中序遍历数组中的下标索引
            ino_root_index = inorder.index(preorder[p_start])
            # 当前树的左子树结点个数
            leftNum = ino_root_index - i_start
            # 当前树的右子树结点个数
            rightNum = i_end - ino_root_index
            # 构建当前树的根节点
            root = TreeNode(preorder[p_start])   
            # 递归构建当前树的左子树
            root.left = buildTree(p_start+1,p_start+leftNum,i_start,ino_root_index-1)
            # 递归构建当前树的右子树
            root.right = buildTree(p_start+leftNum+1,p_end,ino_root_index+1,i_end)
            return root
        # 返回构建树的结点
        return buildTree(0,length-1,0,length-1)
        
相关推荐
非优秀程序员1 小时前
第05章 播放器大战,暴风、快播与那一夜|《互联网产品恩仇录》
算法
武子康7 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
David猪大卫8 小时前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
vvilkim9 小时前
数据结构类型、区别与应用场景:一篇讲透选型逻辑
数据结构
lisw0513 小时前
Dayhoff的PAM替换矩阵理论评介!
线性代数·算法·矩阵
阿卡好可爱14 小时前
Lua 热更新不生效:改完文件线上仍是旧逻辑,package.loaded 与 upvalue 重绑定详解
数据结构·分布式·后端·架构·lua
一起学安全技术14 小时前
拆大模型 03|注意力的一句话版本:不用论文讲清 Attention
深度学习·算法·语言模型·transformer
Delightrs15 小时前
Rust LeetCode238 算法题解
开发语言·算法·rust
自信1504130575916 小时前
重生之从0开始学习c++之——vector-中篇
c++·学习·算法