主要分析4道问题,来理解树的DFS,这4道题目都是关于树的路径的,基本是一道题演化而来的。而且模板基本一致。
题目描述
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
因为这题比较简单,所以直接给出代码
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root == null)
return false;
return dfs(root,sum);
}
private boolean dfs(TreeNode root,int aim){
if(root == null)
return false;
if(root.left == root.right && aim == root.val) //root.left == root.right为叶子节点
return true;
else
return dfs(root.left,aim - root.val) || dfs(root.right,aim - root.val);
}
}
题目描述
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
同样结合代码
class Solution {
List<List<Integer>> res = new ArrayList<>(); //省去传参的麻烦
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if(root == null) return res;
dfs(root,sum,new ArrayList<>());
return res;
}
private void dfs(TreeNode root,int sum,ArrayList tmp){
if(root == null) {
return;
}
tmp.add(root.val); //判断叶子节点的第二种方法
if(root.left == null && root.right == null && sum == root.val){
res.add(new ArrayList<>(tmp));
tmp.remove(tmp.size() - 1); //要回溯,把刚加入的那个值减去,不然影响下一次的值
return;
}
dfs(root.left,sum - root.val,tmp);
dfs(root.right,sum - root.val,tmp);
tmp.remove(tmp.size() - 1); //执行到这说明要回到父亲节点,所以也要把当前节点的值删除
}
}
题目描述
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
示例 1:
输入: [1,2,3]
1
/ \
2 3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
class Solution {
public int sumNumbers(TreeNode root) {
if(root == null)
return 0;
dfs(root,0);
return res;
}
int res;
private void dfs(TreeNode root,int tmp){
if(root == null) return ;
tmp = tmp * 10 + root.val;
if(root.left == root.right){
res += tmp;
return;
}
dfs(root.left,tmp);
dfs(root.right,tmp);
}
}
题目描述
给出一棵二叉树,其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高
有效位开始的二进制数。例如,如果路径为 0 -> 1 -> 1 -> 0 -> 1,那么它表示二进制数
01101,也就是 13 。
对树上的每一片叶子,我们都要找出从根到该叶子的路径所表示的数字。
以 10^9 + 7 为模,返回这些数字之和。
代码如出一辙
class Solution {
private int res;
int mod = 1000000007;
public int sumRootToLeaf(TreeNode root) {
if(root == null) return 0;
dfs(root,0);
return res % mod;
}
private void dfs(TreeNode root,int tmp){
if(root == null) return;
tmp = (tmp<<1) + root.val;
if(root.left == root.right){
res += tmp;
res = res % mod;
return;
}
dfs(root.left,tmp);
dfs(root.right,tmp);
}
}
聪明的同学可能已经发现它们之间的联系,然后你会不会有这么一个疑问,为什么只有第二题回溯了?
tmp.remove(tmp.size() - 1); //要回溯,把刚加入的那个值减去,不然影响下一次的值
而其他三道题目都没有回溯,这个问题是个很好的问题,首先先明白的是java的传参问题:
1、基本类型作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的
2、对象作为参数传递时,是把对象在内存中的地址拷贝了一份传给了参数。
只有第二道题目用到了List这个对象,而其他的tmp都是基本类型,所以如果你传的tmp是基本类型,
那么父亲节点给左右孩子的tmp都是一样的,也就是执行完(1)对(2)的tmp不影响,所以不需要回溯。
dfs(root.left,tmp); //(1)
dfs(root.right,tmp); //(2)
而看第二道用到的tmp是个对象,那么执行完(1)之后,tmp已经把(1)的结果加入进去了,所以要想(2)不受(1)的影响,要进行回溯。
dfs(root.left,sum - root.val,tmp); //(1)
dfs(root.right,sum - root.val,tmp); //(2)
题目描述
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
该题目的难点在于不一定从根节点开始,也不一定从叶子节点结束,如何解决这个问题,思路就是,对于二叉树的每个节点都当做是二叉树的根节点,也就是再多一层递归,然后找到解的终止条件改一下,因为已经不是到叶子节点计算了。代码如下
class Solution {
private int cnt = 0;
public int pathSum(TreeNode root, int sum) {
if(root == null) return 0;
dfs(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return cnt;
}
private void dfs(TreeNode root,int sum){
if(root == null) return;
sum -= root.val;
if(sum == 0){
cnt++;
}
dfs(root.left,sum);
dfs(root.right,sum);
}
}
树的很多问题都要用DFS去做,树的结构天然就与递归联系,要不要回溯这个问题要看你执行完左孩子之后对右孩子是否影响,务必保证左右孩子是”平等“的,加油。