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

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代表子结点:

pstart_{son} = pstart_{parent} + 1 

pend_{son} = pend_{parent} + leftNum

istart_{son} = istart_{parent} 

iend_{son} = inoRootIndex - 1

右子树参数公式推导

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

pstart_{son} = pstart_{parent} + leftNum + 1

pend_{son} = pend_{parent}

istart_{son} = inoRootIndex + 1

iend_{son} = iend{parent}

3.代码

# 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)
        

  • 38
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小人物₍˄·͈༝·͈˄*₎◞ ̑̑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值