先看问题描述吧:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
我只是看懂了别人的算法,要我自己想,肯定想不出来,今天我就用我自己的语言表述一下我的理解。
首先我们应该明确以下几点:
1.前序遍历序列中,第一个元素二叉树的根节点,后面的若干元素可以分为两个序列,第一个序列是左子树的前序遍历序列(记为序列1),第二个序列是右子树的前序遍历序列(记为序列2)
2.在中序遍历中,根节点所在位置之前的所有节点是二叉树左子树的中序遍历序列(暂且记为序列3),根节点所在位置之后的所有节点是二叉树右子树的中序遍历序列(暂且记为序列4)。
本题的解题思路:
第一步:首先在前序遍历中确定根节点,就是第一个元素。
第二步:根据上一步根节点的值去寻找根节点在中序遍历序列中的位置可以设置为index。
第三步:根据index还有传入递归函数中的序列的起始位置和终止位置来确定下次递归调用时传入的子序列。我这样说可能不是很明白。换一种说法,我们在确定递归函数的时候,应该传入这么几个参数:
前序遍历序列:pre
前序遍历序列起始位置:preStart
前序遍历序列终止位置:preEnd
中序遍历序列:in
中序遍历序列起始位置:inStart
中序遍历序列终止位置:inEnd
我们就是通过每次传入的序列位置不同,来确保一次次传入到递归函数的序列长度的不同,也就是每次传入的序列不同。
我们结合图解释一下:递归函数第一次调用的时候,传入的两个序列都是最长的,就是原始的序列长度。因为第一次传入的时候,preStart = 0;preEnd = pre.length-1;inStart = 0; inEnd = in.length-1;而到了第二次传入的时候。这四个参数就改变了,如下图所示:
我们在进行第二次递归函数调用的时候,(左子树)相当于把序列①和序列③传入;(右子树)相当于把序列②和序列④传入。
结合上图中的几个参数的示意,应该很容易写递归代码了。
private static Node Construct(int[] pre, int preStart, int preEnd,
int[] in, int inStart, int inEnd) {
if(preStart>preEnd||inStart >inEnd) {
return null;
}
if(preStart == preEnd) {
Node root = new Node();
root.value = pre[preStart];
return root;
}
//寻找根在中序遍历序列中的索引
int index = -1;
for(int i = inStart;i<=inEnd;i++) {
if(in[i] == pre[preStart]) {
index = i;
break;
}
}
Node root = new Node();
root.value = pre[preStart];
//System.out.println(index);
root.leftNode = Construct(pre,preStart+1,preStart+index-inStart,
in,inStart,index-1);
root.rightNode = Construct(pre, preStart+(index-inStart)+1,
preEnd, in, index+1, inEnd);
return root;
}
public static void main(String[] args) {
int[] pre = {1,2,4,7,3,5,6,8};
int[] in = {4,7,2,1,5,3,8,6};
Node root = Construct(pre,0,pre.length-1,in,0,in.length-1);
printTree(root);
}
//中序遍历
public static void printTree(Node root) {
if(root!=null) {
printTree(root.leftNode);
System.out.print(root.value+" ");
printTree(root.rightNode);
}
}