参考自:《剑指Offer——名企面试官精讲典型编程题》
以及博客:https://www.cnblogs.com/edisonchou/p/4741099.html
题目:重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8, 6},则重建出二叉树并输出它的头结点。
主要思路:在二叉树的前序遍历序列中,第一个数字就是树根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此,通过扫描中序遍历序列,找出根节点位置,进而划分该根节点的左右子树。最后,递归分别重建左子树和右子树。
例如:
1
/ \
2 3
/ / \
4 5 6
\ /
7 8
前序遍历序列:{1,2, 4, 7, 3, 5, 6, 8 }
中序遍历序列:{4, 7, 2, 1,5, 3, 8, 6 }
根节点:1
左子树前序遍历序列:2, 4, 7
左子树中序遍历序列:4, 7, 2
右子树前序遍历序列:3, 5, 6, 8
右子树中序遍历序列:5, 3, 8, 6
然后递归分别重建左子树和右子树。
关键点:前序遍历特点:第一个元素为根节点。中序遍历特点:根节点左边为左子树,右边为右子树。递归。
时间复杂度:O(树长度),注:时间主要消耗在查找根节点位置
public class ReConstructBinaryTree
{
public static void main(String[] args)
{
int[] preOrder = {1, 2, 4, 7, 3, 5, 6, 8};
int[] inOrder = {4, 7, 2, 1, 5, 3, 8, 6};
// 1
// / \
// 2 3
// / / \
// 4 5 6
// \ /
// 7 8
TreeNode result = reconstructBinaryTree(preOrder, inOrder);
}
public static TreeNode reconstructBinaryTree(int[] preOrder, int[] inOrder)
{
if (preOrder == null || inOrder == null) return null;
return rebuild(preOrder, inOrder, 0, preOrder.length - 1, 0, inOrder.length - 1);
}
/**
* @param preOrder 前序遍历序列
* @param inOrder 中序遍历序列
* @param preStart 前序遍历开始位置
* @param preEnd 前序遍历结束位置
* @param inStart 中序遍历开始位置
* @param inEnd 中序遍历结束位置
* @return
*/
public static TreeNode rebuild(int[] preOrder, int[] inOrder, int preStart, int preEnd,
int inStart, int inEnd)
{
if (preStart > preEnd) return null;
//前序遍历第一个元素就是根节点
int rootVal = preOrder[preStart];
int inRootIndex = inStart; //中序遍历中的根节点位置
while (inRootIndex <= inEnd)
{
//在中序遍历中查找根节点位置
if (rootVal == inOrder[inRootIndex]) break;
else inRootIndex++;
}
int leftLength = inRootIndex - inStart; //左子树长度
TreeNode rootNode = new TreeNode(rootVal); //创建根节点
int preLeftEnd = preStart + leftLength;
//创建左子树
rootNode.left = rebuild(preOrder, inOrder, preStart + 1, preLeftEnd,
inStart, inRootIndex - 1);
//创建右子树
rootNode.right = rebuild(preOrder, inOrder, preLeftEnd + 1, preEnd,
inRootIndex + 1, inEnd);
return rootNode;
}
}