给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22, 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
1、函数参数:当前树的根结点、当前累积的路径和,目标路径和;返回值为空 2、终止条件:该结点为空 3、单层逻辑:如果该结点不为空,则在累积路径和上加上该结点的值。如果该结点是叶子结点,判断此时的累积路径和与目标路径和是否相同,如果相同则将全局变量ifHas改为true,认为能够找到路径和为目标值的路径。然后返回父结点,回溯到上一个状态,参数会自动更正为上一个状态的参数。接下来就是按顺序对该结点的左右孩子进行遍历
这个思路是有问题的,因为它只在叶子结点之后才回溯,这是不合理的,但是也能AC。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool ifHas=false; void traversal(TreeNode* cur,int target,int Mysum) { if(cur==NULL) return; if(cur!=NULL) Mysum+=cur->val; //如果是叶子结点,则进行比较 if(cur->left==NULL && cur->right==NULL) { //如果和目标结果相同 if(Mysum == target) ifHas=true; return; } if(cur->left) { traversal(cur->left,target,Mysum); } if(cur->right) { traversal(cur->right,target,Mysum); } return ; } bool hasPathSum(TreeNode* root, int sum) { //if(root == NULL) return false; int Mysum=0; traversal(root,sum,Mysum); return ifHas; } };1、函数参数、返回值确定 之前有个结论:如果需要搜索整棵二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径就要及时返回。 本题并不需要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
bool traversal(TreeNode* cur,int count)2、终止条件确定 在如何统计一条路径和的方法上,代码随想录使用递减的方法,让计数器count初始为目标和,然后每次减去遍历路径结点上的数值。如果最后count==0,同时到了叶子结点的话,说明了找到目标和。如果遍历到了叶子结点,cout不为0,就是没找到
if(!cur->left && !cur->right && count == 0) return true; //遇到叶子结点,并且计数为0 if(!cur->left && !cur->right) return false; //遇到叶子结点,没有找到合适的边,直接返回3、确定单层递归的逻辑 因为终止条件是判断也自己诶单,所以递归过程中就不要让空结点进入递归了。 递归函数的返回值为true的话说明了找到了合适的路径,应该立刻返回
if(cur->left) { count -=cur->left->val; //遇到叶子结点返回true,则直接返回true; if(traversal(cur->left,count)) return true; count +=cur->left->val; //回溯,撤销处理结果 } if(cur->right) { count -=cur->right->val; //遇到叶子结点返回true,则直接返回true; if(traversal(cur->right,count)) return true; count +=cur->right->val; //回溯,撤销处理结果 } return false; class Solution { public: bool traversal(TreeNode* cur,int count) { if(!cur->left && !cur->right && count == 0) return true; //遇到叶子结点,并且计数为0 if(!cur->left && !cur->right) return false; //遇到叶子结点,没有找到合适的边,直接返回 if(cur->left) { count -=cur->left->val; //遇到叶子结点返回true,则直接返回true; if(traversal(cur->left,count)) return true; count +=cur->left->val; //回溯,撤销处理结果 } if(cur->right) { count -=cur->right->val; //遇到叶子结点返回true,则直接返回true; if(traversal(cur->right,count)) return true; count +=cur->right->val; //回溯,撤销处理结果 } return false; } bool hasPathSum(TreeNode* root, int sum) { if(root == NULL) return false; return traversal(root,sum-root->val); } };如果使用栈模拟递归的话对于回溯如何处理? 此时栈里面的一个元素不仅要记录该结点指针,还要记录从头结点到该结点的路径数值总和。 这里使用pair结构来存放栈里面的元素。(第一次用这个结构) 定义为:
pair<TreeNode*,int> pair<结点指针,路径数值>
为栈中的一个元素。 使用栈模拟前序遍历;
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool hasPathSum(TreeNode* root, int sum) { if(root == NULL) return false; //此时栈里面要放的是pair<结点指针,路径数值> stack<pair<TreeNode*,int>> st; st.push(pair<TreeNode*,int>(root,root->val)); while(!st.empty()) { pair<TreeNode*,int> node = st.top(); st.pop(); //如果这个结点是叶子结点,同时该结点的路径数值等于sum,那么就返回true if(!node.first->left &&!node.first->right && sum == node.second ) return true; //右结点,压进去一个结点的时候,将该结点的路径数值也记录下来 if(node.first->right) { st.push(pair<TreeNode*,int>(node.first->right,node.second+node.first->right->val)); } //右结点,压进去一个结点的时候,将该结点的路径数值也记录下来 if(node.first->left) { st.push(pair<TreeNode*,int>(node.first->left,node.second+node.first->left->val)); } } return false; } };给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
同样的道理,Mysum作为函数参数每次返回的时候会自动更正,不需要手动回溯。 完成一次子结果,就将子结果送入paths中。 path需要手动回溯。
path.push_back(cur->left->val); traversal(cur->left,target,Mysum); path.pop_back();同时需要注意,在一开始要将根结点送入path中,因为在我们的递归函数中只对左右孩子进行push_back()
class Solution { public: vector<vector<int>> paths; vector<int> path; void traversal(TreeNode* cur,int target,int Mysum) { if(cur==NULL) return; if(cur!=NULL) { Mysum+=cur->val; } //如果是叶子结点,则进行比较 if(cur->left==NULL && cur->right==NULL) { //如果和目标结果相同 if(Mysum == target) { paths.push_back(path); } return; } if(cur->left) { path.push_back(cur->left->val); traversal(cur->left,target,Mysum); path.pop_back(); } if(cur->right) { path.push_back(cur->right->val); traversal(cur->right,target,Mysum); path.pop_back(); } return ; } vector<vector<int>> pathSum(TreeNode* root, int sum) { if(root==NULL) return {}; int Mysum=0; path.push_back(root->val); traversal(root,sum,Mysum); return paths; } };工程实践上一定要clear,但是由于力扣后台测试数据,每次都是新new一个对象
