Leetcode关于树的路径问题(DFS)

主要分析4道问题,来理解树的DFS,这4道题目都是关于树的路径的,基本是一道题演化而来的。而且模板基本一致。

leetcode112

题目描述

给定如下二叉树,以及目标和 sum = 225
             / \
            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);
    }
}

leetcode113

题目描述

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 225
             / \
            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);   //执行到这说明要回到父亲节点,所以也要把当前节点的值删除
    }
}

leetcode129

题目描述

给定一个二叉树,它的每个结点都存放一个 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);
    }
}

leetcode129

题目描述

给出一棵二叉树,其上每个结点的值都是 01 。每一条从根到叶的路径都代表一个从最高
有效位开始的二进制数。例如,如果路径为 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)

LeetCode437. 路径总和 III

题目描述

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

该题目的难点在于不一定从根节点开始,也不一定从叶子节点结束,如何解决这个问题,思路就是,对于二叉树的每个节点都当做是二叉树的根节点,也就是再多一层递归,然后找到解的终止条件改一下,因为已经不是到叶子节点计算了。代码如下

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去做,树的结构天然就与递归联系,要不要回溯这个问题要看你执行完左孩子之后对右孩子是否影响,务必保证左右孩子是”平等“的,加油。

你可能感兴趣的:(leetcode)