【力扣 113. 路径总和 II】 dfs/回溯两种解法

本文探讨了在给定二叉树和目标和的情况下,寻找所有从根节点到叶子节点路径总和等于目标和的路径问题。通过深度优先搜索(DFS)和回溯两种方法进行解析,对比了它们的效率差异。

题目说明:

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

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

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

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

返回:

[
[5,4,11,2],
[5,8,4,5]
]

思路
  1. 遍历这棵树,使用临时集path并记录路径。
  2. 递归过程: 将当前结点加入临时集合, 并用sum减掉当前结点的值 ,继续遍历其左右子树 。
  3. 递归终止条件: 抵达叶子节点。 此时判断是否满足条件。如果是,就加入结果集。
深度优先遍历(dfs)

dfs 就是一路走到头,碰到叶子节点才会返回。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int sum)
    {
        List<List<Integer>> list = new ArrayList<> ();
        List<Integer> path  = new ArrayList<Integer>() ;
        if(root==null) 
        	return list;
        dfs(root , sum , path , list) ;
        return list ;
    }
    private void dfs(TreeNode root ,int sum , List<Integer> path ,List<List<Integer>> list  )
    {
        if(root ==null)
               return  ;  
               // 递归过程: 将当前值加入临时值 
        path.add(root.val) ;
        sum -=root.val ;
        //递归结束条件: 遍历到叶子节点 
        if(root.left ==null && root.right == null)
        {//如果已经是叶子节点了
            if(sum==0)
            {
                list.add(new ArrayList<> (path)) ;
            }
            return ;
        }
                            
        dfs(root.left , sum ,new ArrayList<Integer>(path) , list) ;
        dfs(root.right, sum , new ArrayList<Integer> (path) ,list) ;
    }
}
//其实就是树的遍历
回溯

回溯的特点, 就是在遍历到叶子节点后,会返回上一级 。此时应该将叶结点从临时集中移除

在这里插入图片描述


class Solution {


    public List<List<Integer>> pathSum(TreeNode root, int sum)
    {

        List<List<Integer>> list = new ArrayList<> ();

        List<Integer> path  = new ArrayList<Integer>() ;
        if(root==null) return list;

        back(root , sum , path , list) ;
        return list ;
    }
    private void back(TreeNode root ,int sum , List<Integer> path ,List<List<Integer>> list  )
    {
        if(root ==null)
               return  ;
        path.add(root.val) ; 
        if(root.left ==null && root.right == null && sum == root.val  )
        {//如果已经是叶子节点了

            list.add(new ArrayList<> (path)) ;
           
        }
        back(root.left , sum-root.val ,path , list) ;
        back(root.right, sum-root.val , path ,list) ;
        path.remove( path.size() -1   ) ;  // 回溯法的体现。 
     }
}


总结

两者的代码比较相似, 由于回溯算法不需要每次都创建新的临时集合,效率较高

大家可以思考为什么要dfs中每一次都要创建新的集合, 而回溯算法只需要在添加时才需要? 临时集合path中的元素究竟发生了什么变化?
博主也是萌新, 不能很好的解释这个问题, 如果你看到这里,并且有思路的,欢迎分享和交流。

关于程序顺序的设计

有时候一个程序的代码顺序会使得整个程序运行结果完全不同, 因此,逻辑思维非常的重要 ,先做那一步,再做那一步,在设计时要思考清楚。

博主本人在做这道题时, 就因为把递归过程放在递归终止条件之后,导致最后的叶结点没有加入到临时集合,折腾了很久。 参看了评论区中的答案后,才明白过来。

学习递归和二叉树,刚开始是十分费劲的,这点博主深有体会,但是,做对一道题的喜悦也是无语伦比的, 博客有几个月没有更新了,虽然没有什么人看, 但接下一直在写,无论是对是错,暴露问题才是解决问题的前提。 接下来会将自己在3,4,5月做二叉树和递归相关的总结和题目集合起来更新。希望有所得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值