想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
442,剑指 Offer-回溯算法解二叉树中和为某一值的路径_算法

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。



示例:

给定如下二叉树,以及目标和 sum = 22,

5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1

返回:

[

[5,4,11,2],

[5,8,4,5]

]



提示:

  • 节点总数 <= 10000


回溯算法

这题没说sum是正数还是负数,也没说树中节点的值有没有负数。我们要做的是从根节点到叶子节点遍历他所有的路径,返回他所有路径中和等于sum的节点,这里有两种实现方式,一种是减,一种是加。减就是从根节点开始,用sum不断的减去遍历到的每一个节点,一直到叶子节点,在减去叶子节点之前查看sum是否等于叶子节点,如果等于说明我们找到了一组,画个图看一下
442,剑指 Offer-回溯算法解二叉树中和为某一值的路径_算法_02

来看下代码

public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, sum, new ArrayList<>(), result);
return result;
}

public void dfs(TreeNode root, int sum, List<Integer> list,
List<List<Integer>> result) {
//如果节点为空直接返回
if (root == null)
return;
//因为list是引用传递,为了防止递归的时候分支污染,我们要在每个路径
//中都要新建一个subList
List<Integer> subList = new ArrayList<>(list);
//把当前节点值加入到subList中
subList.add(new Integer(root.val));
//如果到达叶子节点,就不能往下走了,直接return
if (root.left == null && root.right == null) {
//如果到达叶子节点,并且sum等于叶子节点的值,说明我们找到了一组,
//要把它放到result中
if (sum == root.val)
result.add(subList);
//到叶子节点之后直接返回,因为在往下就走不动了
return;
}
//如果没到达叶子节点,就继续从他的左右两个子节点往下找,注意到
//下一步的时候,sum值要减去当前节点的值
dfs(root.left, sum - root.val, subList, result);
dfs(root.right, sum - root.val, subList, result);
}

上面只是对二叉树的深度优先搜索(DFS),并没有使用回溯,之前讲递归的时候提到过为了防止分支污染我们还可以把使用过的值在返回的时候把它给remove掉,这就是大家常提的回溯算法,也可以看下之前讲的​​426,什么是递归,通过这篇文章,让你彻底搞懂递归​​,看下代码

public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, sum, new ArrayList<>(), result);
return result;
}

public void dfs(TreeNode root, int sum, List<Integer> list,
List<List<Integer>> result) {
//如果节点为空直接返回
if (root == null)
return;
//把当前节点值加入到list中
list.add(new Integer(root.val));
//如果到达叶子节点,就不能往下走了,直接return
if (root.left == null && root.right == null) {
//如果到达叶子节点,并且sum等于叶子节点的值,说明我们找到了一组,
//要把它放到result中
if (sum == root.val)
result.add(new ArrayList(list));
//注意别忘了把最后加入的结点值给移除掉,因为下一步直接return了,
//不会再走最后一行的remove了,所以这里在rerurn之前提前把最后
//一个结点的值给remove掉。
list.remove(list.size() - 1);
//到叶子节点之后直接返回,因为在往下就走不动了
return;
}
//如果没到达叶子节点,就继续从他的左右两个子节点往下找,注意到
//下一步的时候,sum值要减去当前节点的值
dfs(root.left, sum - root.val, list, result);
dfs(root.right, sum - root.val, list, result);
//我们要理解递归的本质,当递归往下传递的时候他最后还是会往回走,
//我们把这个值使用完之后还要把它给移除,这就是回溯
list.remove(list.size() - 1);
}

上面是减的方式,我们再来看一个加的方式,其实他就是从根节点开始到叶子节点把这个路径上的所有节点都加起来,最后查看是否等于sum,画个图看一下

442,剑指 Offer-回溯算法解二叉树中和为某一值的路径_路径_03

代码就很简单了,来看下

public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, sum, 0, new ArrayList<>(), result);
return result;
}

public void dfs(TreeNode root, int sum, int toal, List<Integer> list,
List<List<Integer>> result) {
//如果节点为空直接返回
if (root == null)
return;
//把当前节点值加入到list中
list.add(new Integer(root.val));
//没往下走一步就要计算走过的路径和
toal += root.val;
//如果到达叶子节点,就不能往下走了,直接return
if (root.left == null && root.right == null) {
//如果到达叶子节点,并且sum等于toal,说明我们找到了一组,
//要把它放到result中
if (sum == toal)
result.add(new ArrayList(list));
//注意别忘了把最后加入的结点值给移除掉,因为下一步直接return了,
//不会再走最后一行的remove了,所以这里在rerurn之前提前把最后
//一个结点的值给remove掉。
list.remove(list.size() - 1);
//到叶子节点之后直接返回,因为在往下就走不动了
return;
}
//如果没到达叶子节点,就继续从他的左右两个子节点往下找
dfs(root.left, sum, toal, list, result);
dfs(root.right, sum, toal, list, result);
//我们要理解递归的本质,当递归往下传递的时候他最后还是会往回走,
//我们把这个值使用完之后还要把它给移除,这就是回溯
list.remove(list.size() - 1);
}



总结

原理很简单,就是从根节点到所有叶子节点路径上的结点值,总和是不是等于sum,如果等于就说明找到了一组。这里要注意因为list是引用传递,在往下传递的过程中不能干扰其他路径,有两种方式可以解决,一种是每个路径都新建一个list,一种就是使用回溯算法,就是在当前路径把当前值使用过之后,跳到其他路径的时候要把那个值给移除掉,防止带到下一个分支,给下一个分支造成干扰。

442,剑指 Offer-回溯算法解二叉树中和为某一值的路径_LeetCode_04