#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 ListbinaryTreePaths(TreeNode root) { List ans = new ArrayList<>(); binaryTreePaths2(root, "", ans); return ans; } // string相加太慢了 // 只是想说明下,直接使用String为什么不需要回溯,因为使用String的+的时候已经生成了一个新的对象了 // 后面下一次递归传值的时候是新对象传入 private void binaryTreePaths(TreeNode root, String res, List 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> ans = new linkedList<>(); public List
> pathSum(TreeNode root, int targetSum) { linkedList
res = new linkedList<>(); pathSum(root, targetSum, res); return ans; } // 深度优先搜索+回溯 private void pathSum(TreeNode root, int targetSum, linkedList 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 res) {
if (root == null) return;
// 每次递归进来都重新new一个集合,这样能保证参数res不会在每次递归中相互干扰,也就不用回溯
linkedList 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的情况



