关键思路:
1. 前序遍历序列的第一个数,即为根。
2. 前序遍历的整个序列可以分为3个连续部分:根,左子树,右子树。
3. 中序遍历的整个序列可以分为3个连续部分:左子树,根,右子树。
有了上面3个思路,开始模拟一下程序:
1. 根据preorder序列,找到root
2. 根据root,在inorder中确定左右子树
3. 根据inorder中左右子树的长度,在preorder中确定左右子树
4. 递归子树的前序、中序序列,为root构建左右子树
递归实现如下:
/**
* 根据前序、中序遍历,构建二叉树
* 关键思路:
* 前序、中序遍历结果,都分为三个连续区域:
* 前序:根,左子树,右子树
* 终须:左子树,根,右子树
*
* @author line
* @date 2019年2月17日 下午8:46:50
*/
public class Solution105 {
/**
* 1. 根据preorder确定root
* 2. 根据root和inorder确定左右子树
* 3. 分别挑出左右子树,重复1.
*/
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0)
return null;
return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
/**
* 递归构建左右子树
*/
private TreeNode build(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd) {
TreeNode root = new TreeNode(pre[preStart]); // 这里写成这样,就要提前判断空指针!
int pos = findRoot(root.val, in, inStart, inEnd);
int leftLen = pos - inStart; // 左子树长度
int rightLen = inEnd - pos; // 右子树长度
if (leftLen > 0)
root.left = build(pre, preStart + 1, preStart + leftLen, in, inStart, inStart + leftLen - 1);
if (rightLen > 0)
root.right = build(pre, preEnd - rightLen + 1, preEnd, in, inEnd - rightLen + 1, inEnd);
return root;
}
/**
* 根据传入的root.val,寻找root在inorder序列中的位置,以此找出左右子树
*/
private int findRoot(int rootVal, int[] in, int inStart, int inEnd) {
for (int i = inStart; i <= inEnd; i++) {
if (in[i] == rootVal)
return i;
}
return -1; // ERROR! 题目没有问题的话不应该走到这里
}
}
后序、中序也一样,就不做了。呵呵。
有啥错误的地方还请大神指正!!
扩展一下:
1. 前序、后序能否唯一构建一颗二叉树?
不能。因为不能够根据根的位置确定左右子树,比如斜树的情况。
2. 前序遍历结果相同的树,就一定相同吗?
不一定。可以归结到仅根据前序遍历能否唯一构建一颗二叉树的问题。中、后序同理。