# 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder
和inorder
均 无重复 元素inorder
均出现在preorder
preorder
保证 为二叉树的前序遍历序列inorder
保证 为二叉树的中序遍历序列
# 思路分析:
这个题目的意思是给了你 一颗二叉树它的 前序,中序遍历结果了
比如说这颗二叉树的前序遍历结果是:1,2,4,3,6,7
这颗二叉树的中序遍历结果是:4,2,1,6,3,7
根据上面两个二叉树的遍历结果 二叉树创建出来应该是如下样子:
我们分析一下 还原二叉树的 过程:
我们通过 前序遍历 就看得知 root 节点肯定是 1 ,但是我们继续从 前序遍历中找的 话 我们就很难分辨出哪个是 root 的左子树 和 右子树了,虽然我们看上面完整的二叉树我们得知了 root 的左子树和 右子树是 2,3。但是 还可能有一种情况使它 的 左子树 和 右子树为 2,6,所以我们不能 根据前序遍历确定它这个树 长什么样子
这时我们再结合中序遍历的结果,中序遍历的特点是:先打印完 左子树 然后就是 中值,最后是 右子树,我们再看 中序遍历的结果:4,2,1,6,3,7。从 前序遍历中我们已知了 root 节点肯定是 1 那么 从中序遍历中看的话,1 是 root 而 1 左边就是 左子树的节点,而 1 的右边就是 右子树的节点了
所以根据 中序遍历 我们确定了 1 的左子树分别是: 2,4。1 的右子树 分别是:3,6,7
通过上面的了解得出如下:
根 1 根 1
pre inf
左 2,4 左 4,2
右 3,6,7 右 6,3,7
通过上面列出的我们发现这好像是一个子问题
如果我们不看上面的东西了,我们就看这次 2,4 这个结果 你怎么分辨出 哪个是根节点,哪个是左 / 右子树?
答:
我们先从 根节点 查看 我们从 前序遍历中 找到 第一个元素就是根节点 也就是 2,随后我们看 中序遍历 的 4 在 2 的左边说明了 4 是 左子树 得到答案如下:
2
/
4
那我们再看 结果为:6,3,7 的呢?
我们先看前序遍历中 3 是 第一个元素 那么它就是根节点,确定了根节点,我们再看中序遍历 6 在左边 那它就是左子树,7 在 3 的右边 那 7 就是 右子树 ,得到答案如下:
3
/ \
6 7
就这样的向下 找 最后 节点为 null 了,我们就可以停止子问题的递归了。
所以这道题 比较直接的 方法就是 使用递归的方法,根据两组遍历结果 去 确定根 确定左边有哪些,确定右边有哪些。知道没办法再次切分为止
# 代码实现:
public class BinaryDemo02 { | |
public static void main(String[] args) { | |
int preOrderArray[] = {1, 2, 4, 3, 6, 7}; | |
int infixOrderArray[] = {4, 2, 1, 6, 3, 7}; | |
BinaryTree b = new BinaryTree(); | |
HeroNode heroNode = new BinaryDemo02().buildTree(preOrderArray, infixOrderArray); | |
b.setRoot(heroNode); | |
LinkedList<HeroNode> s = new LinkedList<>(); | |
HeroNode root = heroNode; | |
while(root != null || ! s.isEmpty()) { | |
if(root != null) { | |
s.push(root); | |
System.out.println("前序遍历:" + root); | |
root = root.getLeft(); | |
}else { | |
HeroNode pop = s.pop(); | |
root = pop.getRight(); | |
} | |
} | |
} | |
public HeroNode buildTree(int preOrder[], int inOrder[]) { | |
if(preOrder.length == 0 || inOrder.length == 0) return null; | |
// 通过前序遍历我们获取 root 节点 的值 | |
int rootValue = preOrder[0]; | |
// 将 root 节点 赋值给 节点类 | |
HeroNode root = new HeroNode(rootValue); | |
for (int i = 0; i < inOrder.length; i++) { | |
// 循环 如果 当前索引值等于 root 节点说明找到了 root 节点的索引位置了 | |
if (inOrder[i] == rootValue) { | |
// 从 0 到 i - 1 就是左子树 | |
//copyOfRange 根据 区间 复制 一个数组 参数:inOrder 要复制的数组 区间:从 0 到 i-1 (含头不含尾 所以 i 就是 i - 1 的效果) | |
int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);// [4. 2] | |
// 从 i + 1 到 inOrder.length - 1 就是右子树 | |
int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);// [6, 3, 7] | |
// 从 1 到 i - 1 就是左子树 因为需要 略过 0 索引的值 因为它就是 根节点 而我们要找的是 4,2 中谁是根 这时我们需要前序遍历的结果来判断 | |
int[] preLeft = Arrays.copyOfRange(preOrder, 1, i + 1);// [2, 4] | |
// 从 i + 1 到 preOrder.length - 1 就是右子树 | |
int[] preRight = Arrays.copyOfRange(preOrder, i + 1, preOrder.length);// [3, 6, 7] | |
root.setLeft(buildTree(preLeft, inLeft));// 2 | |
root.setRight(buildTree(preRight, inRight));// 3 | |
break; | |
} | |
} | |
return root; | |
} | |
} |
打印结果:
前序遍历:1
前序遍历:2
前序遍历:4
前序遍历:3
前序遍历:6
前序遍历:7