23 二叉树中和为某一值的路径 + 回溯法深入总结
题目描述
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:原来的我是将借口定义为tmp传值的形式,这样多次拷贝数组效率不高,所以改成传引用的形式。
到达二叉树叶子节点的时候,值减为0,那么这条路径就是我们需要的结果。如果到了叶子节点不满足要求,那么下次是空节点,直接返回了。
回溯法的理解:因为想整个递归过程只使用一个tmp数组存储所有的符合条件的路径结果,所以需要使用回溯法,在进行下次递归之前,需要将状态返回到上一步。
看下面第一种思路:
这里遇到叶子节点会有return,这时应该回溯,还有就是中间的左右节点需要回溯,想一下整个递归的过程,首先一直-> left,到达叶子节点后开始回溯,pop_back一个,转向right节点,然后就是中间部分的回溯,对应最后一行的pop_back。
/* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: void helper(vector<vector<int> > &result,vector<int> &tmp,TreeNode* root,int expectNumber){ if(root == nullptr){ return; } expectNumber = expectNumber - root->val; tmp.push_back(root -> val); if(root -> left == nullptr && root -> right == nullptr){//必须叶子节点才返回 if(expectNumber == 0){ result.push_back(tmp); tmp.pop_back(); return; } else{ tmp.pop_back(); return; } } helper(result,tmp,root -> left,expectNumber); helper(result,tmp,root -> right,expectNumber); tmp.pop_back(); } vector<vector<int> > FindPath(TreeNode* root,int expectNumber) { vector<vector<int> > result; vector<int> tmp; if(root == nullptr){ return result; } helper(result,tmp,root,expectNumber); return result; } };
其实就是每次return要修改一个共有的东西,push_back了,下次递归之前就必须pop_back.这里就因为有return要终止程序了,而前面有一个push_back的操作,所有后面需要pop_back。
可以这么修改:
/* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: void helper(vector<vector<int> > &result,vector<int> &tmp,TreeNode* root,int expectNumber){ if(root == nullptr){ return; } expectNumber = expectNumber - root->val; tmp.push_back(root -> val); if(expectNumber == 0 && root -> left == nullptr && root -> right == nullptr){//必须叶子节点才返回 result.push_back(tmp); } helper(result,tmp,root -> left,expectNumber); helper(result,tmp,root -> right,expectNumber); tmp.pop_back(); } vector<vector<int> > FindPath(TreeNode* root,int expectNumber) { vector<vector<int> > result; vector<int> tmp; if(root == nullptr){ return result; } helper(result,tmp,root,expectNumber); return result; } };
可以联想其他的回溯法的题目,比如permtation,subsets这些问题都可以
下面一个程序是牛客上的某个答案,为什么没有明显的递归基也可以呢?
因为左右节点有判断是否为空,如果都为空那么整个程序就会顺序执行完毕,整个程序就终止了,这也是一种递归基。
class Solution { vector<vector<int> >allRes; vector<int> tmp; void dfsFind(TreeNode * node , int left){ tmp.push_back(node->val); if(left-node->val == 0 && !node->left && !node->right) allRes.push_back(tmp); else { if(node->left) dfsFind(node->left, left-node->val); if(node->right) dfsFind(node->right, left-node->val); } tmp.pop_back(); } public: vector<vector<int> > FindPath(TreeNode* root,int expectNumber) { if(root) dfsFind(root, expectNumber); return allRes; } };