思路:
前序遍历中根永远是在最前,所以用来找根节点
中序遍历根永远在中间,只要知道哪个是根,那么其左边是这个根的左子树,右边是右子树。
也就是说我们只需要重复这个过程直到根节点是叶节点就可以了(实际判定是在这下一步即前序数组为空),这种明显就是递归。
有点像分治法,每次递归设计的变量都变少(本题中是前序数组),直到最后少到一定条件(本题中是前序数组为null)就能结束递归,本题中是(return null来结束)。但一般分治法的分治是刻意分的,相当于是构造的,而这个很自然地把全部的前序,中序数组分成左、右子树的前序和中序,省去了构造过程。
Solution.java:
package execise;
import execise.TreeNode;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length==0){
return null;
}
TreeNode root=new TreeNode(pre[0]);
int root_position = 0;
for(int i=0;i<in.length;i++){
if(in[i]==pre[0]){
root_position=i;
break;
}
}
int [] pre_left = new int[root_position];
int [] pre_right=new int[pre.length-root_position-1];
int [] in_left=new int[root_position];
int [] in_right=new int[pre.length-root_position-1];
for(int i=0;i<root_position;i++){
pre_left[i]=pre[i+1];
in_left[i]=in[i];
}
for(int i=0;i<in.length-root_position-1;i++){
pre_right[i]=pre[i+root_position+1];
in_right[i]=in[i+root_position+1];
}
root.left=reConstructBinaryTree(pre_left,in_left);
root.right=reConstructBinaryTree(pre_right,in_right);
return root;
}
public static void main(String[] args) {
Solution solution=new Solution();
int [] pre={1,2,4,7,3,5,6,8};
int [] in={4,7,2,1,5,3,8,6};
TreeNode root=solution.reConstructBinaryTree(pre, in);
while(root!=null){
System.out.println(root.val);
if(root.val==4){
System.out.println(root.right.val);
}
root=root.left;
}
}
}
TreeNode.java:
package execise;
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
下面看看我复习算法基础这本书时写的分治法相关的求数组中最大值的分治算法:
package exercise1;
public class Solution {
public int MAX(int [] str){
if(str.length<1){
return 0;
}
if(str.length==1){
return str[0];
}
else if(str.length==2){
if(str[0]>=str[1]){
return str[0];
}
else{
return str[1];
}
}
else{
int mid=str.length/2;
int [] str_l=new int[mid+1];
int [] str_r=new int[str.length-mid-1];
for(int i=0;i<=mid;i++){
str_l[i]=str[i];
}
for(int i=0;i<str.length-mid-1;i++){
str_r[i]=str[i+mid+1];
}
if(MAX(str_l)>MAX(str_r)){
return MAX(str_l);
}
else{
return MAX(str_r);
}
}
}
public static void main(String[] args) {
int [] str={2,5,9,6,7,2,9,3,124,2,65,6};
System.out.println(new Solution().MAX(str));
}
}
这个就是自己给它分成两段来递归,前面的是一个,两个这种能计算的情况,后面不能计算的地方就递归,整个最精髓的就
if(MAX(str_l)>MAX(str_r)){
return MAX(str_l);
}else{
return MAX(str_r);
}这一句,这就相当于一个树不断向左分叉直到不能再分(即为叶节点),然后返回这个叶节点的根向右分叉,因为是左右均分的所以左边不能分了那右边必定不能分,不能分代表数组只有一个或两个元素,即MAX()函数已经有返回值了,有返回值就能比较,然后把较大值返回给根,根继续向右分支,这里的右边是可能能再分的,因为左边已经分了,不过不影响,即使分也是重新这么个循环,右边的这个根肯定能返回到值。然后重复这一过程,就能得到最大值。
有趣的是,我写这段代码的时候可没能想到这么多,只不过是凭直觉往上写的,这段分析也是自己画树才写出来的,所以递归还是很有趣的东西。
再来个简单的例子,但是做的时候没想到用递归
青蛙跳阶梯,能跳一格或两格,求跳n格的方法种数。
首先一格 return1
两格 return2。
三格以上递归实现
return JumpFloor(target-1)+JumpFloor(target-2);
拆分的思路是总数为最后一步是跳了一格还是两格两种情况的和.
总结一下分治法的思想就是先考虑情况可以直接计算的情况,然后再把不能直接计算的情况分割成可以计算的情况。
需要思考的点就是怎么出递归以及怎么return。