【Leetcode二叉树属性九】112. 路径总和 113. 路径总和 II (有关于递归函数返回值的讨论)



关于递归函数返回值的讨论

1.需要搜索全局513. 找树左下角的值就没有返回值
2.只需要找出一条符合条件的路径就需要返回值,比如bool

在这里插入图片描述


Leetcode112

1.问题描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.解决方案

解法一:递归

大致思路呢其实很简单就是遍历所有根到叶子的路径,找到一条就算成功
1.根绝上面的讨论返回值肯定是bool了
2.这个return true/false的过程也是向上传递true/false的过程,我希望这个可以一点一点推导几遍
3.终止条件找到叶子如果符合就true,否则就false
4.递归调用的时候有很浓厚的回溯痕迹,backtracking(root->left,targetSum,curSum+root->val)

class Solution {
public:
    bool backtracking(TreeNode* root,int targetSum,int curSum){
        if(root== nullptr) return false;
        if(root->left== nullptr&&root->right== nullptr){
            if(curSum+root->val==targetSum) return true;
            else return false;
        }

        if(backtracking(root->left,targetSum,curSum+root->val)== true) return true;
        if(backtracking(root->right,targetSum,curSum+root->val)== true) return true;

        return false;
    }
    bool hasPathSum(TreeNode* root, int targetSum) {
        return backtracking(root,targetSum,0);
    }
};

java写法基本上一样和c++

public class lc112 {
    public boolean back(TreeNode root, int targetSum , int sum) {
        if(root==null) return false;
        if(root.left==null&&root.right==null){
            if(sum+root.val==targetSum) return true;
            else return false;
        }

        if(back(root.left, targetSum, sum+root.val)) return true;
        if(back(root.right, targetSum, sum+root.val)) return true;
        return false;
    }
    public boolean hasPathSum(TreeNode root, int targetSum) {
        return back(root, targetSum, 0);
    }
}


解法二:迭代

其实就是利用栈模拟前序遍历的迭代,有点繁琐,这里暂时就不给出了


Leetcode113

1.问题描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.解决方案

大致思路呢很简单,就是遍历所有路径符合就加入结果集
1.首先正如上面的讨论,因为要遍历整个树,所以递归函数不需要返回值
2.遇到叶子如果符合就加入,等等没什么可说的,但是这个回溯很重要!!!
3.记录path,可以放成全局变量,也可以作为递归函数的参数,因为这样回溯时方便一点,但是回溯就没那么明显了
4.说实话这个回溯你得好好想想到底时怎么回溯的,我们path.push_back(root->val),进入左子树递归时,加入了左子树的结点后,再递归右子树,这里看起来没回溯是不是就错了呀,实则不然,这正是将path作为函数参数的特点,就是虽然进入左子树会对path进行改变,但是由于path不是全局便利也不是引用类型,所以转入左子树的path不过是拷贝构造的结果,左子树递归完进入右子树时path还是原来的那个path,这不就回溯了嘛,牛批啊!

path.push_back(root->val);
backtracking(root->left,targetSum,path,curSum+root->val);
backtracking(root->right,targetSum,path,curSum+root->val);

4.如果path是全局变量,这才是显示的回溯,怕自己忘了写出来安心一点

path.push_back(cur->left->val);
backtracking(cur->left,curSum);
path.pop();


class Solution {
public:
    vector<vector<int>> ans;
    void backtracking(TreeNode* root,int targetSum,vector<int> path,int curSum){
        if(root== nullptr) return;
        if(root->left== nullptr&&root->right== nullptr){
            if(curSum+root->val==targetSum){
                path.push_back(root->val);
                ans.push_back(path);
            }
            return;
        }
        
        path.push_back(root->val);
        backtracking(root->left,targetSum,path,curSum+root->val);
        backtracking(root->right,targetSum,path,curSum+root->val);
    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        vector<int> path;
        backtracking(root,targetSum,path,0);
        return ans;
    }
};


java的写法和cpp有所不同,区别在于c++的vector< int > path是走拷贝构造,完全是两个对象,而List< Integer > path从始至终都是一个对象,多个引用,所以分析一下具体的回溯过程,就知道,需要显示回溯

1.既然java始终都是一个对象,那其实在函数参数和全局的path没有区别

public class lc113 {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public void back(TreeNode root, int targetSum, int sum, List<Integer> path) {
        path.add(root.val);
        sum += root.val;
        if(root.left==null&&root.right==null){
            if(sum==targetSum){
                ans.add(new ArrayList<>(path));
            }
            return;
        }
        if(root.left!=null){
            back(root.left, targetSum, sum, path);
            path.remove(path.size()-1);
        }
        if(root.right!=null){
            back(root.right, targetSum, sum, path);
            path.remove(path.size()-1);
        }
    }
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if(root==null) return ans;
        back(root, targetSum, 0, new ArrayList<>());
        return ans;
    }
}
class lc113_1 {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> path = new ArrayList<>();
    public void back(TreeNode root, int targetSum, int sum) {
        path.add(root.val);
        sum += root.val;
        if(root.left==null&&root.right==null){
            if(sum==targetSum){
                ans.add(new ArrayList<>(path));
            }
            return;
        }
        if(root.left!=null){
            back(root.left, targetSum, sum);
            path.remove(path.size()-1);
        }
        if(root.right!=null){
            back(root.right, targetSum, sum);
            path.remove(path.size()-1);
        }
    }
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if(root==null) return ans;
        back(root, targetSum, 0);
        return ans;
    }
}

2.上面两种都是在各自的回溯函数中,add各自的节点,然后还有另外一直回溯方式,显得更加清楚,那就是在回溯前add对应的节点,然后函数结束后再回溯,两种都可以,我更喜欢下面这种

class lc113_2 {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    List<Integer> path = new ArrayList<>();
    public void back(TreeNode root, int targetSum, int sum) {
        if(root.left==null&&root.right==null){
            if(sum==targetSum){
                ans.add(new ArrayList<>(path));
            }
            return;
        }
        if(root.left!=null){
            path.add(root.left.val);
            sum += root.left.val;
            back(root.left, targetSum, sum);
            path.remove(path.size()-1);
            sum -= root.left.val;
        }
        if(root.right!=null){
            path.add(root.right.val);
            sum += root.right.val;
            back(root.right, targetSum, sum);
            path.remove(path.size()-1);
            sum -= root.right.val;
        }
    }
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        if(root==null) return ans;
        else path.add(root.val);
        back(root, targetSum, root.val);
        return ans;
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值