关于树的前序遍历、中序遍历、后序遍历:
https://www.jianshu.com/p/1c50b23fcc30
总结:
1) 前、中、后是相对于根节点的
2)当知道前序+中序序列,可以推出树结构,
当知道中序+后序序列,可以推出树结构,
当知道前序+后序序列,无法推出树结构。
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
链接:https://www.nowcoder.com/questionTerminal/8a19cbe657394eeaac2f6ea9b0f6fcf6?answerType=1&f=discussion
来源:牛客网
前序+中序分解过程图示如下(王道数据结构P120)
思路:
- 由先序序列第一个
pre[0]
在中序序列中找到根节点位置gen
- 以
gen
为中心遍历0~gen
左子树- 子中序序列:
0~gen-1
,放入vin_left[]
- 子先序序列:
1~gen
放入pre_left[]
,+1
可以看图,因为头部有根节点
- 子中序序列:
gen+1~vinlen
为右子树- 子中序序列:
gen+1 ~ vinlen-1
放入vin_right[]
- 子先序序列:
gen+1 ~ vinlen-1
放入pre_right[]
- 子中序序列:
- 由先序序列
pre[0]
创建根节点 - 连接左子树,按照左子树子序列递归(
pre_left[]
和vin_left[]
) - 连接右子树,按照右子树子序列递归(
pre_right[]
和vin_right[]
) - 返回根节点
第一个版本的代码:O(n^2)
没接触过树结构这块,看了一下上面的关于树的前序、中序、后续遍历的资料,自己看着序列画了几棵树,感觉有点思路。
大概能总结出来是要先在前序序列中找顶点,接着在中序序列中找到对应index,可以作为划分顶点左右子树的结点,将左右分成两部分继续递归,修改了几次,自己测试是没问题的。
但是写出的方法是O(n^2)的,主要原因在于没有发现前序序列的截取规律,这里想到了前序序列可能能够截取,但是没有想通截取的长度应该是多少。于是第一个版本是双重循环的,没想到提交上去也能过哈哈哈哈,看来测试用例的序列没有太长。
import java.util.Arrays;
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(in.length == 0){
return null;
}
int preIndex = 0;//前序遍历index
int inIndex = 0;//中序遍历index
boolean falg = false;
for (int i = 0; i < pre.length; i++) {
for (int j = 0; j < in.length; j++) {
//在pre中找到第一个能与in序列匹配上的元素
if(pre[i]==in[j]){
preIndex = i;
inIndex = j;
System.out.println("当前顶点:"+pre[preIndex]+",in总长度:"+in.length+",index位置:"+inIndex);
falg = true;
break;
}
}
if(falg){
break;
}
}
//当前结点
TreeNode tempNode = new TreeNode(pre[preIndex]);
tempNode.left = reConstructBinaryTree(pre, Arrays.copyOfRange(in, 0, inIndex));
tempNode.right = reConstructBinaryTree(pre,Arrays.copyOfRange(in, inIndex+1, in.length));
return tempNode;
}
修改后的代码:O(n)
测试过了以后看了一下解题思路,发现pre也是可以根据inIndex截取的,对比上面的图想了一下:A结点为根节点,
在中序序列中,左子树——A——右子树,
在前序序列时,顶点A——左子树——右子树,
那么pre中,A后面跟着的左子树的长度必然与中序中A左边的长度是相同的。知道了A在中序序列中的位置inIndex,则可以根据此索引来截取pre了。
修改后的代码也简洁了很多:
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0 || in.length == 0){
return null;
}
int inIndex = 0;
TreeNode tempNode = new TreeNode(pre[0]);//每次只要找pre中第一位即可
for (int i = 0; i < in.length; i++) {
if(pre[0]==in[i]){
inIndex = i;
tempNode.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, inIndex + 1), Arrays.copyOfRange(in, 0, inIndex));
tempNode.right = reConstructBinaryTree(Arrays.copyOfRange(pre, inIndex + 1, pre.length),Arrays.copyOfRange(in, inIndex+1, in.length));
break;
}
}
return tempNode;
}
另外就是还要注意Arrays.copyOfRange的用法,在截取时,左闭右开,所以左边要从需要的那一位开始截取。在截取in时,曾误将左边的界限:inIndex+1写为inIndex,结果造成了死循环。