#LeetCode每日一题【二叉树专题】
-
在java中基本类型都是值传递,而非基本类型都是引用传递;当其作为参数在递归过程中,需要注意分支污染问题,以及是否需要回溯的问题
-
int作为参数传递
因为int是值传递,每次递归调用方法都是传入一个值,递归调用结束之后,并不会影响原来的值,即不存在分支污染问题
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传入了
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会同步保留该修改,即递归中分支污染问题,需要回溯的场景
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的情况