手把手教你如何重建二叉树(超精彩配图)
一、二叉树的重建
在LeetCode中有这么一道算法题:《重建二叉树》
(一)面试题07. 重建二叉树
输入某二叉树的前序遍历
和中序遍历
的结果
,请重建该二叉树
。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
限制:
0 <= 节点个数 <= 5000
(二)分析该题目
从题目中可以看到,给出了两个数组,一个书前序遍历二叉树得出的结果,一个是中序遍历二叉树得出的结果。
利用这两个结果,来还原出一颗二叉树。
首先你要知道什么是前序遍历和中序遍历。你还要知道,给你一颗二叉树,使用这些遍历的算法怎么得到遍历的结果,不然是没办法继续完成重建二叉树的。
如果你还不输入二叉树的遍历,可以看这篇文章: 《我是怎么一步一步调试出来二叉树的遍历(超精彩配图),二叉树遍历再也不用愁了》
先来看一下前序遍历
如下图所示,前序遍历就是:
根
—左
—右
再来看一下中序遍历:
终须遍历则是:
左
—根
—右
最终就得到的这样的数组:
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
如果你够细心的话,会发现前序
遍历的第一个是3
,而这个3就是
整颗二叉树的根节点
;
在看一下中序的结果:
如下图所示,如果我们把中序中3所在的位置的左右两部分分割开,就得到了根节点3的所有左边和右边的节点。
[9],[3],[15,20,7]
不难发现,如果我们以此类推的话,就可以还原出一颗二叉树。
(三)java代码实现
package tree;
import java.util.Arrays;
/**
* @Auther: truedei
* @Date: 2020 /20-6-9 12:00
* @Description: 二叉树重建
*/
public class Solution {
/**
* 根据前序和中序遍历的二叉树的结果构建二叉树
* @param preorder 前序遍历结果的数组
* @param inorder 中序遍历结果的数组
* @return 返回一个构建好的二叉树
*/
static public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null || preorder.length==0)
return null;
//1,获取树的根节点的value值
TreeNode root = new TreeNode(preorder[0]);
//2,查找每一次根节点在中序遍历结果中的位置
int index = findIndex(preorder[0],inorder);
//3,构建left左子树
// root.left = buildTree(左子树前序数组,左子树中序数组)
root.left = buildTree(Arrays.copyOfRange(preorder,1,index + 1 ),
Arrays.copyOfRange(inorder,0,index));
//4,构建reght右子树
// root.right=buildTree(右子树前序数组,右子树中序数组)
root.right = buildTree(Arrays.copyOfRange(preorder,index+1,preorder.length),
Arrays.copyOfRange(inorder,index+1,inorder.length));
return root;
}
/**
* 查找根的index(在中序中的位置)的函数
* @param preorderData 节点
* @param inorder 每一次的中序数组
* @return 索引位置
*/
static public int findIndex(int preorderData, int[] inorder){
for (int i = 0; i < inorder.length; i++) {
if(inorder[i]==preorderData)
return i;
}
return 0;
}
public static void main(String[] args) {
TreeNode treeNode = buildTree(new int[]{3,9,20,15,7}, new int[]{9,3,15,20,7});
theFirstTraversal(treeNode);
}
//先序遍历 用于测试最终的结果是否正确
public static void theFirstTraversal(TreeNode root) {
System.out.println(root.val);
if (root.left != null) { //使用递归进行遍历左孩子
theFirstTraversal(root.left);
}
if (root.right != null) { //递归遍历右孩子
theFirstTraversal(root.right);
}
}
}
核心代码:
用于递归构建二叉树
/**
* 根据前序和中序遍历的二叉树的结果构建二叉树
* @param preorder 前序遍历结果的数组
* @param inorder 中序遍历结果的数组
* @return 返回一个构建好的二叉树
*/
static public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null || preorder.length==0)
return null;
//1,获取树的根节点的value值
TreeNode root = new TreeNode(preorder[0]);
//2,查找每一次根节点在中序遍历结果中的位置
int index = findIndex(preorder[0],inorder);
//3,构建left左子树
// root.left = buildTree(左子树前序数组,左子树中序数组)
root.left = buildTree(Arrays.copyOfRange(preorder,1,index + 1 ),
Arrays.copyOfRange(inorder,0,index));
//4,构建reght右子树
// root.right=buildTree(右子树前序数组,右子树中序数组)
root.right = buildTree(Arrays.copyOfRange(preorder,index+1,preorder.length),
Arrays.copyOfRange(inorder,index+1,inorder.length));
return root;
}
/**
* 查找根的index(在中序中的位置)的函数
* @param preorderData 节点
* @param inorder 每一次的中序数组
* @return 索引位置
*/
static public int findIndex(int preorderData, int[] inorder){
for (int i = 0; i < inorder.length; i++) {
if(inorder[i]==preorderData)
return i;
}
return 0;
}
一定有十万个为什么,为什么要这样写,其实写法有很多。接下来咱们一起来调试一下,看看执行时是什么样子的。
(四)IDEA调试代码,详细查看构建的过程
我这里使用的工具是IDEA。
为了方便调试,便于查看每一次执行的结果,我们先打上断点。
然后Debug运行此java文件,如下图所示,就是最初运行的结果
进一步了解一下:
可以看到现在传入的前序数组和中序数组分别为: 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7]
一定要有印象哦
下图就是看到的我们接着上一个图下一步之后的结果。
在这个操作了,我们起作用的是
TreeNode root = new TreeNode(preorder[0]);
我们拿到了前序数组0号位置的数据,至于为什么一定要拿到前序数组中的0号位置的数据,可以看一下下面的图。解释的很清楚了。
下一步之后,就会看到下面的结果;
这里我们要去查找拿到的3在中序数组中的所在的位置 至于为什么,请看下图
这里就不在说明Arrays.copyOfRange()的作用了,请看(五)附Arrays.copyOfRange()的用法,给大家准备好了。
接下来就要使用拿到的index,去构建一个左节点了
继续下一步
继续下一步
继续下一步
开始构造3右边所有的节点:
这样我们就拿到了20的左节点
现在就可以构造出最后一个节点了,不过因为是顺序执行的,所以省略n步
验证是否正确:
到此为止,结束了。
经过Debug一遍,心里一定很清晰了。
(五)附Arrays.copyOfRange()的用法
与使用System.arraycopy进行数组复制类似的, Arrays提供了一个copyOfRange方法进行数组复制。
Arrays.copyOfRange()的格式如下:
第一个参数表示源数组
第二个参数表示开始位置(取得到)
第三个参数表示结束位置(取不到)
copyOfRange(int[] original, int from, int to)
实例代码:
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
// 不同的是System.arraycopy,需要事先准备好目标数组,并分配长度。
// copyOfRange只需要源数组就就可以了,通过返回值,就能够得到目标数组了。
// 除此之外,需要注意的是 copyOfRange 的第3个参数,表示源数组的结束位置,是取不到的。
// copyOfRange(int[] original, int from, int to)
// 第一个参数表示源数组
// 第二个参数表示开始位置(取得到)
// 第三个参数表示结束位置(取不到)
int[] b = Arrays.copyOfRange(a, 0, 3);
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + "t");//18 62 68
}
// 溢位复制
System.out.println();
int[] C = Arrays.copyOfRange(a, 0, 7);
for (int i = 0; i <C.length; i++) {
System.out.print(C[i] + "t");//18 62 68 82 65 9 0
}
}
}
这里参考地址:
Java基础学习——复制数组の——Arrays.copyOfRange()方法讲解
- AngularJS in Action读书笔记5(实战篇)——在directive中引入D3饼状图显示
- WCF中并发(Concurrency)与限流(Throttling)体系深入解析系列[共7篇]
- AngularJS in Action读书笔记6(实战篇)——bug hunting
- FreeMarker模板开发指南知识点梳理
- WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
- WCF如何克服HTTP传输协议的局限提供对不同消息传输模式的实现
- H5手游大事件:腾讯上线“微信小游戏”!支持群分享与内购
- 我所理解的Remoting(2):远程对象生命周期的管理[上篇]
- 谈谈分布式事务(Distributed Transaction)[共5篇]
- SQLXML初体验:用XML代替T-SQL来操作数据库
- 自己动手写可视化软件(代码已开源)
- 探秘Tomcat——连接篇
- 微信小游戏正式上线,H5游戏迎新机遇
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android基础控件RadioGroup使用方法详解
- Android采用消息推送实现类似微信视频接听
- Android BottomSheet实现可拉伸控件
- Android自定义RecyclerView实现不固定刻度的刻度尺
- Android RIL使用详解
- 详解Android aidl的使用方法
- Android 进度条自动前进效果的实现代码
- Android 系统服务TelecomService启动过程原理分析
- Android圆角头像工具类详解
- Android实现系统消息推送
- Android仿微信QQ聊天顶起输入法不顶起标题栏的问题
- Android实现倒计时的按钮效果
- 腾讯位置服务实现轨迹回放
- 有一种遗憾,是跑程序却没用上_______
- Android使用注解代替枚举节省系统内存开销的方法