LeetCode重建二叉树详解[通俗易懂]

LeetCode重建二叉树详解[通俗易懂]LeetCode重建二叉树详解题目描述原理分析(1)大致思路(2)细节阐述代码实现(1)主函数(2)递归函数参数区间的决定递归结束的条件总结题目描述原理分析(1)大致思路下面讲解一下,前序遍历+中序遍历如何确定一个唯一的二叉树。关于二叉树的基本知识,请看二叉树的基本操作及联系。对此就不再过多重复。对于前序遍历顺序:根、左子树、右子树;对于中序的遍历顺序:左子树、根、右子树。所以通过前序遍历,我们获取前序第一个结点就是这个树的根,再在中序遍历中找到该结点的位置。在中序中,根的左边全部的是属于根左子

大家好,又见面了,我是你们的朋友全栈君。

题目描述

在这里插入图片描述

原理分析

(1)大致思路

下面讲解一下,前序遍历+中序遍历如何确定一个唯一的二叉树。关于二叉树的基本知识,请看二叉树的基本操作及联系。对此就不再过多重复。对于前序遍历顺序:根、左子树、右子树;对于中序的遍历顺序:左子树、根、右子树。所以通过前序遍历,我们获取前序第一个结点就是这个树的根,再在中序遍历中找到该结点的位置。在中序中,根的左边全部的是属于根左子树的结点,根的右边全是属于根的右子树的结点。
详细如图:
在这里插入图片描述做完第一步之后,我们会发现,我们目前只具体确定了哪一个是根节点,哪些结点分别属于左右子树。但是由于树的递归特性。属于左子树的结点仍然符合前序遍历,中序遍历特点的。所以我们就是需要对刚刚分离出来的两部分分别再次用上述的方法,确定根节点,确定哪些结点属于左子树,哪些结点属于右子树。一次类推,直到结束。这就是这道题的大致思路。

(2)细节阐述

这道题还是有不少值得思考的地方。
1、我们如何表示哪些结点是属于左子树,右子树?
答:前序、中序的给出都vector,所以我们可以通过下标确定范围来做到。前序:【根,左子树,右子树】。中序:【左子树,根,右子树】。他们都是三种类别结点都是集中在一起,很好区分。
2、递归的结束条件是什么?
答:这个还是要结合具体代码分析,目前可以确定的是,当我们控制范围时,如果出现范围不合法(不存在)的情况就说明已经没有左子树或者右子树了,就要返回。

代码实现

注:一般在我的题解中,范围控制,代码这样书写的原因都会通过注释的方式写在对应代码旁边,帮助读者理解分析代码,这样更有针对性。因此,如果读者对于前面的题目分析有疑惑或者不理解的地方,请仔细阅读代码及旁边注释,一定会对你有所启发,帮助你的理解!!

(1)主函数

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { 
   
	//_buildTree本题因为需要递归实现,因此需要借助一个递归函数。
       return _buildTree(preorder,0,preorder.size()-1,inorder,0,inorder.size()-1);
    }

我在这里要详细说明一下这个递归函数的各个参数及作用
第一个参数:就是前序遍历的vector,没有太多需要解释的
第二个参数:preStart,就是在子问题种前序的起始位置。详细的说,就是我们在确定根以后,我们就要递归左右子树,左右子树的起始位置是哪里就要确定。
第三个参数:preEnd:就是在子问题种前序的终止位置,这样就保证preStart与preEnd之间是子问题的结点序列。
第四个参数就是中序遍历的vector
第五个参数:inStart,就是在子问题中中序遍历的起始位置
第六个参数:inEnd,就是在子问题种中序遍历的结束位置

(2)递归函数

	TreeNode* _buildTree(vector<int>& preorder,int preStart,int preEnd,vector<int>&inorder,int inStart,int inEnd)
    { 
   
        if(preStart>preEnd)
        { 
   
            return nullptr;
        }
        //我们可以直接确定根节点,所以我们首先创建出根节点
        TreeNode* root = new TreeNode(preorder[preStart]);
        //找到根节点后,我们拿着根节点的值在中序遍历中找到对应位置,区分左右子树
        for(int i=inStart;i<=inEnd;i++)
        { 
   
        	//找到根节点所在的中序遍历的位置
            if(inorder[i] == preorder[preStart])
            { 
   
            	//由于递归函数完成子问题树的构建,所有让大问题的root左右子树分别链接即可
                root->left = _buildTree(preorder,preStart+1,preStart+i-inStart,inorder,inStart,i-1);
                root->right = _buildTree(preorder,preStart+i-inStart+1,preEnd,inorder,i+1,inEnd);
            }
        }
        return root;
    }

参数区间的决定

关键就是在递归子问题的时候传参数是多少。
在这里插入图片描述在这里插入图片描述
size:size是左子树中的结点个数所以size = i-inStart
所以:root->left = _buildTree(preorder,preStart+1,preStart+i-inStart,inorder,inStart,i-1);
xxxxxxroot->right = _buildTree(preorder,preStart+i-inStart+1,preEnd,inorder,i+1,inEnd);
读者自行比较即可理解

递归结束的条件

接下来就是判断如何结束递归,就是递归函数中的第一个if。之前我们提到过,如果子问题子树的区间不存在就可以结束循环了,那么怎么才叫不存在呢?
我们刚刚获得了每一个子树的前序的范围【preStart,preEnd】,如果preStart==preEnd时候,就说明子树还有一个结点,仍然需要循环,但是有没有可能preStart>preEnd呢?答案是肯定的。我们发现递归子问题的时候preStart = preStart+1,preStart不断增大,而递归子问题时preEnd = preStart+size-1。
分析比较得:当子树只有一个结点(size == 1)是,preStart == preEnd。再递归一次后,size == 0,因此preStart > preEnd。就说明没有子树了,可以返回nullptr了。
所以得到代码:if (preStart>preEnd) return nullptr;

总结

xxxx看到二叉树问题,我们首相应该想到的就是函数递归,因而二叉树具有很好的“递归特性”,每一个子树都是二叉树,都满足树的特性,子问题具有一样的特性就可以使用递归算法。其次我们应该明确知道,二叉树的“前、中、后,层序遍历”,并且知道他们之间的关系联系以及区别。在有以上的思想以及储备知识后,我们就可以写出具有一定思路的代码逻辑。这道题还有一个比较重要的地方就是控制子问题的前序、中序vector的范围界限。真正保证子问题与原问题的统一
xxxx这就是这道题的完整解析,如果大家有更好的思路,或者我代码中可优化的地方,请指出,我一定虚心学习。希望我们一起学习,一起进步!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/131500.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 软件工程需求分析实验_实验设备管理系统需求分析

    软件工程需求分析实验_实验设备管理系统需求分析一、系统的问题描述1.系统简介每学年要对实验室设备使用情况进行统计、更新。其中:(1)对于已彻底损坏的做报废处理,同时详细记录有关信息。(2)对于由严重问题(故障)的要及时修理,并记录修理日期、设备名、编号、修理厂家、修理费用、责任人等。(3)对于急需修改但又缺少的设备,需以“申请表”的形式送交上级领导请求批准购买。新设备购入后要立即进行设备登记(包括类别、设备名、编号、型号、规格、单价、数量、购置日期、生产厂家、保质期和经办人等信息),同时更新申请表的内容。(4)随时对现有设备及其

    2022年10月13日
    2
  • 5节锂电池升压充电管理芯片型号_锂电池充电管理ic

    5节锂电池升压充电管理芯片型号_锂电池充电管理ic5V升压充电21V五节锂电池升压充电管理芯片HU5911是一款工作于2.7V到6.5V的PFM升压型多节电池充电控制集成电路。HU5911采用恒流和准恒压模式(Quasi-CVTM)对电池进行充电管理,内部集成有基准电压源,电感电流检测单元,控制电路和片外场效应晶体管驱动电路等,具有外部元件少,电路简单等优点。当接通输入电源后,HU5911进入充电状态,控制片外N沟道MOSFET导通,电感电流上升,当上升到外部电流检测电阻设置的上限时,片外N沟道MOSFET截止,电感电流下降,电感中的能量转移到电池中

    2022年9月28日
    1
  • Redis集群搭建(非常详细)

    Redis集群搭建(非常详细)https blog csdn net article details redis 集群搭建在开始 redis 集群搭建之前 我们先简单回顾一下 redis 单机版的搭建过程 下载 redis 压缩包 然后解压压缩文件 进入到解压缩后的 redis 文件目录 此时可以看到 Makefile 文件 编译 redis 源文件 把编译好的 redis 源文件安装到 usr local redis 目录下 如果 local 目录下没有 redis 目录 会自动新建 r

    2025年10月28日
    3
  • EnableDocking[通俗易懂]

    EnableDocking[通俗易懂]CFrameWnd::EnableDockingvoidEnableDocking(DWORDdwDockStyle);參数:dwDockStyle指定框架窗体的哪一边可作为控件条的停靠点,可

    2022年7月2日
    25
  • 基于Html5的移动端APP开发框架「建议收藏」

    基于Html5的移动端APP开发框架「建议收藏」快速增长的APP应用软件市场,以及智能手机的普及,手机应用:Native(原生)APP快速占领了APP市场,成为了APP开发的主流,但其平台的不通用性,开发成本高,多版本开发等问题,一直困扰着专业APP开发企业,和APP服务提供商。安卓和IOS的操作方式,开发模式,界面UI显示方面的差别,也使得原生APP的不同版本体验有很大的区别,光是做兼容性调测,都要花费开发企业不少的时间。近年来,另一种

    2022年6月15日
    168
  • iptable 详解_iptable命令详解1

    iptable 详解_iptable命令详解1-p-protocal[!]protocol:协议-s-source[!]address[/mask]:源地址-d–destination[!]address[/mask]:目的地址-j–jumptarget:-i–in-interface[!][name]:入口-o–out-interface[!][name]:出口-f,–fragment:分片指定-pt…

    2022年5月28日
    76

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号