【从零开始刷leetcode-48】一道结合回溯+前缀和+递归的优秀题目

题目 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

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

示例 1:

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

示例 2:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3

提示:

  • 二叉树的节点个数的范围是 [0,1000]
  • -109 <= Node.val <= 109 
  • -1000 <= targetSum <= 1000 

解法一 递归 + 每层递归做dfs

每次递归返回以root为起点,和为targetSum的路径条数。

如何计算以当前root为起点,和为targetSum的路径条数呢?

这里的思路是做dfs遍历(用的是根左右),用currsum值记录根节点到这里的和,与targetSum相等就cnt++;(需要思考优化)

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        /**
         * 递归,返回以root为起点的 和为targetSum的路径条数
         */ 
        if (root == null) return 0;
        long currSum = 0;
        int cnt = getCnt(root, targetSum, currSum);
        return pathSum(root.left, targetSum) + pathSum(root.right, targetSum) + cnt;
    }

    private int getCnt(TreeNode root, int targetSum, long currSum) {
        if (root == null) return 0;
        int cnt = 0;
        currSum += root.val;
        if (targetSum == currSum) cnt++;
        cnt += getCnt(root.left, targetSum, currSum);
        cnt += getCnt(root.right, targetSum, currSum);
        return cnt;
    }
}

这个方法时间复杂度比较差,因为每个节点都要做一次DFS,所以最大时间复杂度为O(N2)

空间复杂度因为用到递归,考虑递归栈的占用,因为不是平衡二叉树这类数据结构,最差情况下可能是一条的树,最大空间复杂度为O(N)

思考优化 – 前缀和计算子数组和

其实计算路径和本质上就是计算子数组和,之前在数组板块计算子数组和有一种特别好用的方法——前缀和

怎么把数组中的前缀和迁移到树中呢?

试一下构建一棵前缀和树!再加一个<前缀和, 个数>的HashMap

再进行一次dfs,每遍历到一个前缀和,在Map中找出符合要求的个数加上去,递归返回前将这个节点的前缀和在map中去掉。

这样的方法不行,因为HashMap中的其他分支树的前缀和会造成混淆,要想实现必须要层序遍历,太麻烦了~

所以我们让HashMap伴随着递归而变化,而不是提前构建出所有的前缀和个数Map,这样每时每刻map中保存的都是深度优先遍历到这里走过路径的前缀和。这其实就是回溯的思想!

class Solution {
    Map<Long, Integer> map = new HashMap<>();
    public int pathSum(TreeNode root, int targetSum) {
        map.put(0L, 1);
        return dfs(root, 0, targetSum);
    }

    private int dfs(TreeNode root, long currentSum, int targetSum) {
        if (root == null) return 0;
        int cnt = 0;
        currentSum += root.val;
        Integer mapValue = map.get(currentSum - targetSum);
        if (mapValue != null) cnt += mapValue;
        Integer cntCurrSum = map.get(currentSum);
        if (cntCurrSum == null) {
            map.put(currentSum, 1);
        } else {
            map.put(currentSum, cntCurrSum + 1);
        }
        cnt += dfs(root.left, currentSum, targetSum);
        cnt += dfs(root.right, currentSum, targetSum);
        // 回溯
        map.put(currentSum, map.get(currentSum) - 1);
        return cnt;
    }
}

时间复杂度:只需要dfs遍历一次树,为O(N)

空间复杂度:同样为O(N)

小结

递归结合前缀和:通过使用前缀和,我们能够在树的 DFS 遍历中高效地判断是否存在满足条件的路径和。

动态管理 HashMap:在每次遍历到一个节点时,更新当前路径的前缀和并存入 HashMap。在回溯时移除当前节点的前缀和,确保其他分支的路径计算不会受到干扰

回溯思想:通过递归深度优先遍历每条路径,并在回溯时恢复之前的状态,确保在遍历过程中不会有错误的前缀和影响其他路径。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇