赞
踩
前序遍历:根左右 中序遍历:左根右
就不太明白从数组到树的递归怎么写,递归参数是什么,于是看了下评论的解答,自己理解了下
然后具体的范围确定,这里细节有两个点纠结了很久:
(1)先序遍历的左右子树范围切片点
(2)中序遍历的左子树起点为什么不能一直是0
这两个点待会会在bug调试里提到。先讲一下正确的思路:
接下来就是具体的范围确定。
(1)先序第一个值为根值
(2)在中序中找到a[i]=num,则[l2,i-1]为左树,[i+1,r2]为右树
(3)在先序中确定左右树范围:
中序中可以确定左树的节点数量:i-1-l2,先序的分割点假设是x,则先序中左树数量是x-l1-1(第一个点是根要-1)则有
i-1-l2=x-l1-1,求得x=i+l1-l2。
所以先序的左树范围就是[l1,x=i+l1-l2],右树范围是[i+l1-l2+1,r1]。
关于范围:
root.left = Tree(preorder,L1+1,L1+j-L2,inorder,L2,j-1,map);
root.right=Tree(preorder,L1+j-L2+1,R1,inorder,j+1,R2,map);
因为要想到这样算分割点实在有点难,受评论启发,定义一个中间变量来表示左子树的个数,这样就好理解多了.
int num = j-1-L1;
root.left = Tree(preorder,L1+1,L1+1+num,inorder,L2,j-1,map);
root.right=Tree(preorder,L1+1+num+1,R1,inorder,j+1,R2,map);
但是刚刚发现执行了一下会出现问题欸
有点晚了,之后再认真看下代码吧。那就按照原来的算吧。
class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { if(preorder==null|| inorder==null){ return null; } // int L1 = preorder.length; // int L2 = inorder.length; // TreeNode tree =Tree(0,L1,0,L2,preorder,inorder); return Tree(0,preorder.length-1,0,inorder.length-1,preorder,inorder); } public TreeNode Tree( int preorder_left,int preoder_right, int inorder_left, int inorder_right,int[] preorder, int[] inorder) { if (preorder_left > preoder_right){ return null; } int num=preorder[preorder_left]; int a = inorder_left; while(inorder[a]!=num){ a++; } // int a=0; // for(int j=inorder_left;j<inorder_right;j++){ // if(inorder[j]==num){ // a=j; // break; // } // } TreeNode root = new TreeNode(num); // root.left = Tree(preorder_left+1,,inorder_left,a-1,preorder,inorder); // root.right = Tree(preorder_left+a,preorder.length-1,a+1,inorder.length-1,preorder,inorder); root.left = Tree(preorder_left+1,preorder_left + a - inorder_left,inorder_left,a-1,preorder,inorder); root.right = Tree(preorder_left + a - inorder_left+ 1,preoder_right,a+1,inorder_right,preorder,inorder); return root; } }
遍历会提高时间复杂度:
以空间换时间,先将中序遍历序列转换成一个hashMap
class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { HashMap<Integer,Integer> map =new HashMap<>(); for (int i=0 ;i<inorder.length;i++){ map.put(inorder[i],i); } return Tree(preorder,0,preorder.length-1,inorder,0,preorder.length-1,map); } public TreeNode Tree(int[] preorder,int L1,int R1,int[] inorder,int L2,int R2 ,HashMap<Integer,Integer> map){ if(L1>R1 || L2>R2){ return null; } int root_val =preorder[L1]; TreeNode root =new TreeNode(root_val); int j = map.get(root_val); root.left = Tree(preorder,L1+1,L1+j-L2,inorder,L2,j-1,map); root.right=Tree(preorder,L1+j-L2+1,R1,inorder,j+1,R2,map); return root; } }
贴一个评论的有趣代码
class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { if(preorder.length==0||inorder.length==0){ return null; } TreeNode root=new TreeNode (preorder[0]); for(int i=0;i<preorder.length;i++){ if(preorder[0]==inorder[i]){ root.left=buildTree(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i)); root.right=buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length)); break; } } return root; } }
Arrays.copyOfRange(preorder,i+1,preorder.length)#复制一个左闭右开的数组
为什么两个的范围不一样,是因为它实现了一个动态copy的是[1,i+1]就已经把第一个元素(根)删除了,然后因为根在第一次的位置是I 所以左数个数是i-1,copyOfRange 左闭合右开,刚好是一样的
为什么上面那个递归需要算个数呢
i-1-l2=x-l1-1,求得x=i+l1-l2。
是因为传入的是原数组,每次先序的左边界是不一样的。
缺点:copyOfRange 消耗资源,双指针节省空间内存
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。