【LeetCode】递归时值传递、引用传递的注意

#LeetCode每日一题【二叉树专题】

  • 在java中基本类型都是值传递,而非基本类型都是引用传递;当其作为参数在递归过程中,需要注意分支污染问题,以及是否需要回溯的问题

  • int作为参数传递

    因为int是值传递,每次递归调用方法都是传入一个值,递归调用结束之后,并不会影响原来的值,即不存在分支污染问题

    如:LeetCode104求根节点到叶节点数字之和

    	int ans = 0;
    
        public int sumNumbers(TreeNode root) {
            getSum(root, 0);
            return ans;
        }
    
        private void getSum(TreeNode root, int sum) {
            if (root == null) return;
            sum = sum * 10 + root.val;
            if (root.left == null && root.right == null) {
                ans+=sum;
            }
            getSum(root.left, sum);
            getSum(root.right, sum);
        }
    
  • String作为参数传递

    String本身是引用传递,但是他比较特殊,String是final的,每次+或者赋值其实都是生成了一个新的String对象,即在递归过程中也不存在分支污染问题,不需要回溯操作;

    跟上面的int不同:如果有操作,则每次递归调用的时候都生成了一份新的String传入了

    如:LeetCode257二叉树的所有路径

    public List<String> binaryTreePaths(TreeNode root) {
            List<String> ans = new ArrayList<>();
            binaryTreePaths2(root, "", ans);
            return ans;
        }
    
        // string相加太慢了
        // 只是想说明下,直接使用String为什么不需要回溯,因为使用String的+的时候已经生成了一个新的对象了
        // 后面下一次递归传值的时候是新对象传入
        private void binaryTreePaths(TreeNode root, String res, List<String> ans) {
            if (root == null) return;
            if (res.length() == 0) {
                res += root.val;
            } else {
                res = res + "->" + root.val;
            }
            if (root.left == null && root.right == null) {
                ans.add(res);
            }
            binaryTreePaths(root.left, res, ans);
            binaryTreePaths(root.right, res, ans);
        }
    
  • List作为参数传入

    list是最容易犯错的一个点,有时候会很容易忽略将list作为参数传入之后,然后做了相应的修改,原方法的该list已经同步做了修改这点没有意识到。

    在递归中也是一样,递归调用该方法的时候list做了修改,那返回到原来时在使用该list会同步保留该修改,即递归中分支污染问题,需要回溯的场景

    如:LeetCode113路径总和 II

    List<List<Integer>> ans = new LinkedList<>();
    
        public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
            LinkedList<Integer> res = new LinkedList<>();
            pathSum(root, targetSum, res);
            return ans;
        }
    
        // 深度优先搜索+回溯
        private void pathSum(TreeNode root, int targetSum, LinkedList<Integer> res) {
            if (root == null) return;
            res.add(root.val);
            if (root.left == null && root.right == null) {
                // 叶子节点满足最终条件
                if (root.val == targetSum) {
                    // 要new一个集合进去,不然后面会被同步修改
                    ans.add(new LinkedList<>(res));
                }
            }
            pathSum(root.left, targetSum - root.val, res);
            pathSum(root.right, targetSum - root.val, res);
            // 要回溯,把当前的节点从集合中去除
            res.pollLast();
        }
    

    如果不想回溯的话,类似于String那种,可以在每次递归调用的时候生成一份新的list传入,这样每次调用操作的list之间互不影响(但存在集合copy的大量时间空间开销)

    // 深度优先搜索+每次递归都生成一个新集合,保证不会干扰其他递归分支
        private void pathSum2(TreeNode root, int targetSum, LinkedList<Integer> res) {
            if (root == null) return;
            // 每次递归进来都重新new一个集合,这样能保证参数res不会在每次递归中相互干扰,也就不用回溯
            LinkedList<Integer> temp = new LinkedList<>(res);
            temp.add(root.val);
            if (root.left == null && root.right == null) {
                if (root.val == targetSum) {
                    ans.add(temp);
                }
            }
            pathSum2(root.left, targetSum - root.val, temp);
            pathSum2(root.right, targetSum - root.val, temp);
        }
    
  • 总结

    在递归过程中能够快速识别到是否会存在分支污染问题,是否需要回溯操作,尤其注意List的情况

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值