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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • WIFI 常识

    WIFI 常识DSSS(DirectSequenceSpreadSpectrum)直接序列扩频FHSS,跳频技术(Frequency-HoppingSpreadSpectrum)FHSS和DSSS比较跳频扩频(FHSS):跳频扩频(FHSS)技术是通过“伪随机码”的调制,信息的载波受一伪随机序列的控制,使载波工作的中心频率不断跳跃改变,而噪音和干扰信号的中心频率却不会改变,这样,只要收、发信机之间按照固定的数字算法产生相同的“伪随机码”,就可以达到同步,排除了噪音和其它干扰信号。虽然在..

    2022年7月20日
  • php 数组根据值找key,从数组查找key对应的值 – key

    php 数组根据值找key,从数组查找key对应的值 – key…L,`nick`varchar(32)DEFAULTNULL,`reg_date`datetimeDEFAULTNULL,PRIMARYKEY(`id`),KEY`IND_NICK`(`nick`),KEY`IND_REGDATE`(`reg_date`))CREATETABLE`users`(`id`int(11)NOTNULL,`nick`varchar(32)D…

    2022年7月23日
  • SAPI5_SAP-ERP

    SAPI5_SAP-ERP转载自:http://blog.csdn.net/stone0823/article/details/70339694SAPWebIDE是基于Eclipse内核的在线开发IDE,可以使用在线的试用版本,但服务器在德国,访问的网速特别慢。也可以使用PersonalEdition,在本机启动和编写代码。与Eclipse相比,SAPWebIDE有几个

    2022年10月17日
  • [原创]-数据仓库ETL开发

    [原创]-数据仓库ETL开发ETL开发概述ETL是数据仓库的后台,主要包含抽取、清洗、规范化、提交四个步骤,传统数据仓库一般分为四层模型。分层的作用:1.划分ETL阶段工作重心,便于管理2.降低开发和维护成本3.减…

    2022年6月4日
  • linux用命令dpkg,Linux中dpkg命令起什么作用呢?

    linux用命令dpkg,Linux中dpkg命令起什么作用呢?摘要:下文讲述Linux中dpkg的功能说明,如下所示;dpkg是(Debianpackage英文的缩写)dpkg命令功能:用于管理DebianLinux中的包管理(安装,创建,管理软件)的一种实用性工具dpkg命令的语法格式:dpkg[参数]—–常用参数说明——-i:安装软件包-r:删除软件包-l:输出已安装软件包列表-L:输出于软件包关联的文件-c:输出软件包内文件列表–…

    2022年5月21日
  • 动画图解:十大经典排序算法动画与解析,看我就够了!(配代码完全版)[通俗易懂]

    动画图解:十大经典排序算法动画与解析,看我就够了!(配代码完全版)[通俗易懂]排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排序。内部排序是数据记录在内存中进行排序。而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括:时间复杂度与空间复杂度关于时间复杂度:…

    2022年10月9日

发表回复

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

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