题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
关键词:二叉树
题目理解
遍历规则:(根据根节点的位置来确定是,前序、中序、后序)
前序遍历为:根节点——左子树——右子树
中序遍历为:左子树——根节点——右子树
后序遍历为:左子树——右子树——根节点
层序遍历为:把一棵树从上到下,从左到右依次写出来
前序遍历为:(4,7,9,2,1,5,6)
中序遍历为:(7,9,4,1,2,6,5)
后序遍历为:(9,7,1,6,5,2,4)
题目要求:重建(输出root所对应的借点)
题解
原理
根据中序遍历和前序遍历可以确定二叉树,具体过程为:
- 根据前序序列第一个结点确定根结点
- 根据根结点在中序序列中的位置分割出左右两个子序列
- 对左子树和右子树分别递归使用同样的方法继续分解
例如:
前序序列{1,2,4,7,3,5,6,8} = pre
中序序列{4,7,2,1,5,3,8,6} = in
- 根据当前前序序列的第一个结点确定根结点,为 1
- 找到 1 在中序遍历序列中的位置,为 in[3]
- 切割左右子树,则 in[3] 前面的为左子树, in[3] 后面的为右子树
- 则切割后的左子树前序序列为:{2,4,7},切割后的左子树中序序列为:{4,7,2},切割后的右子树前序序列为:{3,5,6,8},切割后的右子树中序序列为:{5,3,8,6}
- 对子树分别使用同样的方法分解
代码实现
链接:https://www.nowcoder.com/questionTerminal/8a19cbe657394eeaac2f6ea9b0f6fcf6?answerType=1&f=discussion
来源:牛客网
/**
* Definition for binary tree
* 二叉树结构:
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.Arrays;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
// (选择)判断是否为空数组
if (pre.length == 0 || in.length == 0) {
return null;
}
// (顺序)前序的第一个点是根节点
TreeNode root = new TreeNode(pre[0]);
// (循环)在中序长度
// (选择)找到前序的根
// (顺序)重复操作,找到子树前序的根
for (int i = 0; i < in.length; i++) {
if (in[i] == pre[0]) {
// 左子树,注意 copyOfRange 函数,左闭右开
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
// 右子树,注意 copyOfRange 函数,左闭右开
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
break;
}
}
return root;
}
}
另一种递归
原理
1、通过前序序列第一个元素确定根节点(例如 1)。
2、通过根节点把中序序列分成两个序列,一个是左子树序列([4,7,2)],一个是右子树序列([5,3,8,6)]。
3、通过左右子树的中序序列可以求出前序遍历的左右子树序列(左:[2,4,7],右:[3,5,8,6])。
4、左右子树的前序序列第一个元素分别是根节点的左右儿子。
5、通过递归重复以上步骤
代码实现
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
// (选择)判断是否为空数组
TreeNode root;
root = rebuildTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
return root;
}
// preStart-preEnd表示前序序列的起始位置,inStart-inEnd也一样
private TreeNode rebuildTree(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd) {
if(preStart > preEnd | inStart > inEnd)
return null;
// 根节点
TreeNode root = new TreeNode(pre[preStart]);
// 寻找根节点在中序序列的位置
for (int i = inStart; i <= inEnd; i++) {
if (in[i] == pre[preStart]) {
// 可以计算出中序序列的左右子树序列为:左:inStart~i -1,右:i+1~inEnd。
// 前序序列的左右子树:左:preStart+1~preStart+i-inStart,右:preStart+i-inStart+1~preEnd
root.left = rebuildTree(pre,preStart+1, preStart+i-inStart,in, inStart, i - 1);
root.right = rebuildTree(pre,preStart+i-inStart+1, preEnd, in, i+1, inEnd);
}
}
return root;
}
总结
二叉树——重建二叉树
1.前序遍历+中序遍历 => 重建二叉树
2.中序遍历+后序遍历 => 重建二叉树
3.前序遍历+后序遍历 => 无法确定二叉树
4·二叉树的遍历方式简单来说有3种方式,前,中,后序遍历,一般采用递归算法,有的可能还存在所谓的层次遍历,就是一层一层的遍历;
5·其实要记忆这东西也很简单,遍历的顺序都是根据根节点的位置来的,根节点在第一个就是前序,根节点在第二位就是中序,根节点在第三位就是后续;
6·注意 copyOfRange 函数,左闭右开