对于二叉树递归,老是有些模糊,思路好想,但是一写代码就会有点问题,对于递归函数的参数不太会设置,导致经常递归都会出问题,今天做了leetcode第105,根据先序和中序序列构造二叉树,思路很清晰,就是先找到根节点,然后在中序中以根节点为界,划分左子树和右子树,然后先序序列往后继续找根节点,以此类推。可是问题是函数参数应该怎么设置呢?虽然好想,最后自己还是写不出来,学习了别人的代码,照着大概写了个。性能并不是太好,照理说我用map映射应该性能会提高很多个档次的啊,可是时间还是只击败了5%。
先序:3 9 20 15 7
中序:9 3 15 20 7
class Solution {
public:
map<int,int> innum;
TreeNode* tree(int prel,int prer,int inl,int inr,vector<int>& preorder, vector<int>& inorder){
for(int i=0;i<preorder.size();i++){
innum[inorder[i]]=i;
}
if(prel>prer||inl>inr)return NULL;
TreeNode* root=new TreeNode(preorder[prel]);
int left=innum[preorder[prel]]-inl;
root->left=tree(prel+1,prel+left,inl,innum[preorder[prel]]-1, preorder,inorder);
root->right=tree(prel+left+1,prer,innum[preorder[prel]]+1,inr,preorder,inorder);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return tree(0,preorder.size()-1,0,preorder.size()-1,preorder,inorder);
}
};
一开始,我的递归代码没有用left,界限都是直接用的map映射的下标,如下:
root->left=tree(prel+1,prel+innum[preorder[prel]],inl,innum[preorder[prel]]-1, preorder,inorder);
root->right=tree(prel+innum[preorder[prel]]+1,prer,innum[preorder[prel]]+1,inr,preorder,inorder);
这是个很严重的思维错误,这种思想就是只想了一层递归导致的,如果二叉树深度超过2,会出现有结点还没处理就越界了。
对于每一棵子树而言,它的所有都是建立在这个子树的基础上的,换种说法,抛开其他的,可以把这个子树看成独立的,不要去看上一个根节点是怎么样的,比如3之后的右子树,这棵右子树的左子树是15,那么15这个节点就应该是以20位根节点的这棵树的左右节点的范围,是相对于3而言,而不应该在整棵树里找范围,所以设置left这个左子树的节点数就很有必要,它记录的是每棵树的左子树的数量。这样才能找出正确的范围,而不至于越界。
可能正常人都觉得递归是很容易想的,很符合逻辑的,但是我觉得,好难啊啊啊!!!每次递归一进去,我就要被绕晕,参数设不对要花很长时间才能找出问题。真不知道要练多少题我才能掌握啊。
根据上面的代码,总结了一些有利于我写二叉树递归代码的方法。
首先,对于会变化的,有规律的缩小的,类似这道题里的子树的范围,应该考虑设为参数;
其次,每棵子树都是相对于根节点的,不能笼统的看,参数要做出相对于根节点的对应的变化;