核心思想

头节点没有父亲,其他节点只有一个父亲的有向无环图,直观理解为发散状 在树上,从头节点出发到任何节点的路径是唯一的,不管二叉树还是多叉树都如此

树型dp在树上做动态规划,依赖关系比一般动态规划简单, 因为绝大部分多数都是父依赖子 只是依赖关系简单,不代表题目简单

树型dp套路

1)分析父树得到答案需要子树的哪些信息

2)把子树信息的全集定义成递归返回值

3)通过递归让子树返回全集信息(一般的递归就是看要不要考虑当前节点)

4)整合子树的全集信息得到父树的全集信息并返回

例题

1、二叉搜索子树的最大键值和

题目描述: 给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。

二叉搜索树的定义如下:

  • 任意节点的左子树中的键值都 小于 此节点的键值。

  • 任意节点的右子树中的键值都 大于 此节点的键值。

  • 任意节点的左子树和右子树都是二叉搜索树。

示例 1:

输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6] 输出:20 解释:键值为 3 的子树是和最大的二叉搜索树。

示例 2:

输入:root = [4,3,null,1,2] 输出:2 解释:键值为 2 的单节点子树是和最大的二叉搜索树。

示例 3:

输入:root = [-4,-2,-5] 输出:0 解释:所有节点键值都为负数,和最大的二叉搜索树为空。

示例 4:

输入:root = [2,1,3] 输出:6

示例 5:

输入:root = [5,4,8,3,null,6,3] 输出:7

提示:

  • 每棵树有 1 到 40000 个节点。

  • 每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。

解题思路:

  • 这个题我们就依据核心思想中的树型dp步骤来写

  • 首先是分析父树得到答案需要子树的哪些信息

    • 首先根据搜索二叉树的定义,左子树都要比当前节点小,右子树都要比当前节点大,那么父节点就需要子树节点的最大值 max,最小值 min

    • 又因为本题是求二叉搜索子树的最大键值和,因此我们还需要一个变量用于表示当前子树是否为二叉搜索子树 isBST,以及子树的二叉搜索子树的最大键值和 maxBstSum

    • 被我忽略的,最后如果一个子树如果是二叉搜索树,那么其二叉搜索子树的最大键值和就为这个子树所有节点的数值和,所以这里还需要一个 sum 来辅助寻找最大键值和

  • 然后将上述信息全集定义成递归返回值

  • 设定递归的到整个树的信息

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 1373 + 二叉搜索子树的最大键值和

🧠 题目关键词(原文关键词)

二叉搜索树,最大键值和

🧩 抽象问题类型(题目本质)

树型dp

🔍 数据规模 / 限制

- 每棵树有 1 到 40000 个节点。 - 每个节点的键值在 [-4 * 10^4 , 4 * 10^4] 之间。

🧭 我的初步思路

树型dp

✅ 正确解法类型

树型dp

❗ 没想到的原因

树型dp题目父节点需要信息自己做的时候少考虑了节点和这个信息

📦 归入的题型分类

树型dp

🧠 触发词(以后遇到就联想)

目标答案在子树中且子树相互独立,父树信息需要子树信息且信息可以压缩为有限个->树型dp

🧪 解法一句话总结

树型dp

代码如下:

 package main.java.class078;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code02_MaximumSumBst  
  * @description: 二叉搜索子树的最大键值和  
  * // 给你一棵以 root 为根的二叉树  
  * // 请你返回 任意 二叉搜索子树的最大键值和  
  * // 二叉搜索树的定义如下:  
  * // 任意节点的左子树中的键值都 小于 此节点的键值  
  * // 任意节点的右子树中的键值都 大于 此节点的键值  
  * // 任意节点的左子树和右子树都是二叉搜索树  
  * // 测试链接 : https://leetcode.cn/problems/maximum-sum-bst-in-binary-tree/  
  * @author: zs宝  
  * @create: 2025-09-19 09:07  
  * @Version 1.0  
  **/public class Code02_MaximumSumBst {  
     // 不要提交这个类  
     public static class TreeNode {  
         public int val;  
         public TreeNode left;  
         public TreeNode right;  
     }  
   
     // 提交如下的方法  
     public static int maxSumBST(TreeNode root) {  
         return f(root).maxBstSum;  
     }  
   
     //分析父树得到答案寻妖那些信息  
     public static class info{  
         //树中最大值  
         int max;  
         //树中节点最小值  
         int min;  
         //所有节点和  
         int sum;  
         //是不是二叉搜索树  
         boolean isBST;  
         //二叉搜索树节点和  
         int maxBstSum;  
         public  info(int a,int b,int c,boolean d,int e){  
             max=a;  
             min=b;  
             sum=c;  
             isBST=d;  
             maxBstSum=e;  
         }  
     }  
   
     public static info f(TreeNode node){  
         if(node==null){  
             return new info(Integer.MIN_VALUE,Integer.MAX_VALUE,0,true,0);  
         }  
         info left=f(node.left);  
         info right=f(node.right);  
         int max=Math.max(node.val,Math.max(left.max, right.max));  
         int min=Math.min(node.val,Math.min(left.min, right.min));  
         boolean isBST= left.isBST && right.isBST && left.max< node.val && right.min> node.val;  
         int sum= left.sum+node.val+right.sum;  
         int maxBstSum=Math.max(left.maxBstSum, right.maxBstSum);  
         if(isBST){  
             maxBstSum=Math.max(sum,maxBstSum);  
         }  
         return new info(max,min,sum,isBST,maxBstSum);  
     }  
 }

2、二叉树的直径

题目描述: 给你一棵二叉树的根节点,返回该树的 直径 。

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。

示例 1:

输入:root = [1,2,3,4,5] 输出:3 解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

示例 2:

输入:root = [1,2] 输出:1

提示:

  • 树中节点数目在范围 [1, 104] 内

  • -100 <= Node.val <= 100

解题思路:

  • 确认父树需要子树的那些信息:子树的直径,子树的高度

  • 递归,直径需要经过当前节点,直径不需要经过当前节点

  • 我的错误,在考虑以当前节点为根节点的数,若直径需要经过当前节点时,那么直径应该为左右子树的高度之和,而我算的是高度之和加一,这是没有好好区分树的高度值与本节计算直径(直径要的是边的个数)的区别

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 543 + 二叉树的直径

🧠 题目关键词(原文关键词)

二叉树,直径,两个节点之间最长路径的 长度

🧩 抽象问题类型(题目本质)

属性dp

🔍 数据规模 / 限制

- 树中节点数目在范围 [1, 104] 内 - -100 <= Node.val <= 100

🧭 我的初步思路

树型dp

✅ 正确解法类型

树型dp

❗ 没想到的原因

直径与树的高度转化错误

📦 归入的题型分类

树型dp

🧠 触发词(以后遇到就联想)

目标答案要考虑子树信息且子树相互独立,父树信息需要子树信息且信息可以压缩为有限个->树型dp

🧪 解法一句话总结

树型dp

代码如下

 package main.java.class078;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code03_DiameterOfBinaryTree  
  * @description: 二叉树的直径  
  * // 给你一棵二叉树的根节点,返回该树的直径  
  * // 二叉树的 直径 是指树中任意两个节点之间最长路径的长度  
  * // 这条路径可能经过也可能不经过根节点 root  
  * // 两节点之间路径的 长度 由它们之间边数表示  
  * // 测试链接 : https://leetcode.cn/problems/diameter-of-binary-tree/  
  * @author: zs宝  
  * @create: 2025-09-19 09:54  
  * @Version 1.0  
  **/public class Code03_DiameterOfBinaryTree {  
   
     public class TreeNode {  
         int val;  
         TreeNode left;  
         TreeNode right;  
         TreeNode() {}  
         TreeNode(int val) { this.val = val; }  
         TreeNode(int val, TreeNode left, TreeNode right) {  
             this.val = val;  
             this.left = left;  
             this.right = right;  
         }  
    }  
     public static int diameterOfBinaryTree(TreeNode root) {  
         return f(root).diameter;  
     }  
     public static class info{  
         int diameter;  
         int height;  
         public info(int a,int b){  
             diameter=a;  
             height=b;  
         }  
     }  
   
     public static info f(TreeNode x){  
         if(x==null){  
             return new info(0,0);  
         }  
         info left=f(x.left);  
         info right=f(x.right);  
         //若直径要经过当前节点。则直径为子树高度之和+1  
         int diameter=left.height+right.height;  
         //若不经过当前节点,则直径为左右子树的最大直径  
         diameter=Math.max(diameter,Math.max(left.diameter,right.diameter));  
         int height=Math.max(left.height,right.height)+1;  
         return new info(diameter,height);  
     }  
   
   
 }

3、在二叉树中分配硬币

题目描述: 给你一个有 n 个结点的二叉树的根结点 root ,其中树中每个结点 node 都对应有 node.val 枚硬币。整棵树上一共有 n 枚硬币。

在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。移动可以是从父结点到子结点,或者从子结点移动到父结点。

返回使每个结点上 只有 一枚硬币所需的 最少 移动次数。

示例 1:

输入:root = [3,0,0] 输出:2 解释:一枚硬币从根结点移动到左子结点,一枚硬币从根结点移动到右子结点。

示例 2:

输入:root = [0,3,0] 输出:3 解释:将两枚硬币从根结点的左子结点移动到根结点(两次移动)。然后,将一枚硬币从根结点移动到右子结点。

提示:

  • 树中节点的数目为 n

  • 1 <= n <= 100

  • 0 <= Node.val <= n

  • 所有 Node.val 的值之和是 n

解题思路:

  • 树上每个结点想要 正好 1 枚硬币

  • 如果某个结点多了/少了,就需要通过边和父子交换;

  • 问:最少多少次交换能把所有结点调成 1 枚。

  • 每条边上的硬币移动次数,等于子树对外的“净盈余/净亏空”

    • 如果某个子树一共 5 枚硬币,但里面有 3 个结点,那么它比理想的 3 枚多出 2 枚。 这 2 枚必须流出去,至少要经过边 2 次。

    • 如果某个子树只有 1 枚硬币,但里面有 3 个结点,那么它缺 2 枚。 这 2 枚必须从外面流进来,也至少要经过边 2 次。

  • 每个子树的盈余/亏空值 = 子树总硬币数 - 子树结点数

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 979 + 在二叉树中分配硬币

🧠 题目关键词(原文关键词)

每个结点 node 都对应有 node.val 枚硬币,整棵树上一共有 n 枚硬币,每个结点上 只有 一枚硬币所需的 最少 移动次数

🧩 抽象问题类型(题目本质)

树型dp

🔍 数据规模 / 限制

- 树中节点的数目为 n - 1 <= n <= 100 - 0 <= Node.val <= n - 所有 Node.val 的值之和是 n

🧭 我的初步思路

树型dp

✅ 正确解法类型

树型dp

❗ 没想到的原因

识别出来了

📦 归入的题型分类

树型dp

🧠 触发词(以后遇到就联想)

“每个子树必须满足条件”“最终答案依赖左右子树信息”“跨边流动/贡献必须由子树汇总” ⇒ 树型 DP

🧪 解法一句话总结

定义 balance = 子树硬币数 − 子树节点数,自底向上累加|

代码如下

 package main.java.class078;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code04_DistributeCoins  
  * @description: 在二叉树中分配硬币  
  * // 给你一个有 n 个结点的二叉树的根结点 root  
  * // 其中树中每个结点 node 都对应有 node.val 枚硬币  
  * // 整棵树上一共有 n 枚硬币  
  * // 在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点  
  * // 移动可以是从父结点到子结点,或者从子结点移动到父结点  
  * // 返回使每个结点上 只有 一枚硬币所需的 最少 移动次数  
  * // 测试链接 : https://leetcode.cn/problems/distribute-coins-in-binary-tree/  
  * @author: zs宝  
  * @create: 2025-09-19 10:38  
  * @Version 1.0  
  **/public class Code04_DistributeCoins {  
     public static class TreeNode {  
         public int val;  
         public TreeNode left;  
         public TreeNode right;  
     }  
   
     public static int ans;  
     public int distributeCoins(TreeNode root) {  
         ans=0;  
         f(root);  
         return ans;  
     }  
   
     public static class info{  
         int coins;  
         int nodes;  
         public info(int a,int b){  
             coins=a;  
             nodes=b;  
         }  
     }  
   
     public static info f(TreeNode x){  
         if(x==null){  
             return new info(0,0);  
         }  
         info left=f(x.left);  
         info right=f(x.right);  
         int nodes=left.nodes+right.nodes+1;  
         int coins=x.val+left.coins+right.coins;  
         ans+=(Math.abs(left.coins-left.nodes)+Math.abs(right.coins-right.nodes));  
         return new info(coins,nodes);  
     }  
   
 }

5、没有上司的舞会

题目描述 某大学有 n 个职员,编号为 1\ldots n。

他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。

现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 r_i,但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。

所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。 输入格式

输入的第一行是一个整数 n。

第 2 到第 (n + 1) 行,每行一个整数,第 (i+1) 行的整数表示 i 号职员的快乐指数 r_i。

第 (n + 2) 到第 2n 行,每行输入一对整数 l, k,代表 k 是 l 的直接上司。 输出格式

输出一行一个整数代表最大的快乐指数。 输入输出样例 #1

输入 #1

 7
 1
 1
 1
 1
 1
 1
 1
 1 3
 2 3
 6 4
 7 4
 4 5
 3 5

输出 #1

 5

说明/提示 数据规模与约定

对于 100\% 的数据,保证 1\leq n \leq 6 \times 10^3,-128 \leq r_i\leq 127,1 \leq l, k \leq n,且给出的关系一定是一棵树。

这个题就是一个变形的打家劫舍问题,十分类似,都是讨论当前节点要不要,要的话下一个节点只能选择不要,当前节点不要的话,那么就像选下一个节点的要与不要的较大值

代码如下

 package main.java.class078;  
   
 import java.io.*;  
 import java.util.Arrays;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code05_Dancing  
  * @description: 没有上司的舞会  
  * // 某大学有n个职员,编号为1...n  
  * // 他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树  
  * // 父结点就是子结点的直接上司  
  * // 现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数  
  * // 但是如果某个职员的直接上司来参加舞会了  
  * // 那么这个职员就无论如何也不肯来参加舞会了  
  * // 所以请你编程计算邀请哪些职员可以使快乐指数最大  
  * // 返回最大的快乐指数。  
  * // 测试链接 : https://www.luogu.com.cn/problem/P1352  
  * // 本题和讲解037的题目7类似  
  * // 链式链接 : https://leetcode.cn/problems/house-robber-iii/  
  * @author: zs宝  
  * @create: 2025-09-19 11:12  
  * @Version 1.0  
  **/public class Code05_Dancing {  
     public static int MAXN=6001;  
     //记录员工快乐值  
     public static int[]nums=new int[MAXN];  
     //链式前向星建图  
     public static int[]head=new int[MAXN];  
     public static int[]next=new int[MAXN];  
     public static int[]to=new int[MAXN];  
     public static int cnt;  
   
     //判断谁是老板  
     public static boolean[]boss=new boolean[MAXN];  
   
     //判断每个节点要与不要能获得的最大快乐值  
     // 动态规划表  
     // yes[i] : i为头的整棵树,在i来的情况下,整棵树能得到的最大快乐值  
     public static int[]yes=new int[MAXN];  
     // 动态规划表  
     // no[i] : i为头的整棵树,在i不来的情况下,整棵树能得到的最大快乐值  
     public static int[]no=new int[MAXN];  
   
     public static int n;  
     public static int boss_one;  
   
     public static void build(){  
         Arrays.fill(head,0,n+1,0);  
         Arrays.fill(boss,0,n+1,true);  
         cnt=1;  
     }  
   
     public static void addEdge(int u,int v){  
         next[cnt]=head[u];  
         to[cnt]=v;  
         head[u]=cnt++;  
     }  
   
     public static void main(String[] args) throws IOException {  
         BufferedReader buffer=new BufferedReader(new InputStreamReader(System.in));  
         StreamTokenizer in=new StreamTokenizer(buffer);  
         PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));  
         while (in.nextToken()!=StreamTokenizer.TT_EOF){  
             n=(int)in.nval;  
             build();  
             for(int i=1;i<=n;i++){  
                 in.nextToken();  
                 nums[i]=(int)in.nval;  
             }  
             for(int i=1,u,v;i<n;i++){  
                 in.nextToken();  
                 v=(int)in.nval;  
                 in.nextToken();  
                 u=(int)in.nval;  
                 addEdge(u,v);  
                 boss[v]=false;  
             }  
             //找到boss  
             for(int i=1;i<=n;i++){  
                 if(boss[i]){  
                     boss_one=i;  
                     break;  
                 }  
             }  
             f(boss_one);  
             out.println(Math.max(yes[boss_one],no[boss_one]));  
   
         }  
         out.flush();  
         out.close();  
         buffer.close();  
     }  
   
     public static void f(int current){  
         yes[current]=nums[current];  
         no[current]=0;  
         //遍历当前节点的所有员工  
         for(int ei=head[current],emplyee;ei>0;ei=next[ei]){  
             emplyee=to[ei];  
             f(emplyee);  
             yes[current]+=no[emplyee];  
             no[current]+=Math.max(yes[emplyee],no[emplyee]);  
         }  
     }  
 }

6、监控二叉树

题目描述: 给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

输入:[0,0,null,0,0] 输出:1 解释:如图所示,一台摄像头足以监控所有节点。

示例 2:

输入:[0,0,null,0,null,0,null,null,0] 输出:2 解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

  1. 给定树的节点数的范围是 [1, 1000]

  2. 每个节点的值都是 0。

解题思路:

  • 这个题主要就是讨论清楚可能的状况

  • 主要时要从树的底往上讨论

  • 空节点该怎算呢,只能算覆盖呀,那么这样一来在一个摄像头没加之前,每个叶子节点开始都是子节点被覆盖了,但是当前节点没有被覆盖,这样的话,讨论的范围就是已经预定好子节点被覆盖了,然后推出其它状态

  • 0:当前节点没有被覆盖,但是子节点已经全部覆盖,

  • 1:当前节点被覆盖,子节点也被覆盖,摄像头未在当前节点上

  • 2:当前节点被覆盖了,子节点也被覆盖了,摄像头在当前节点上

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 968 + 监控二叉树

🧠 题目关键词(原文关键词)

每个摄影头都可以监视其父对象、自身及其直接子对象。最小摄像头数量

🧩 抽象问题类型(题目本质)

树型dp

🔍 数据规模 / 限制

1. 给定树的节点数的范围是 [1, 1000] 2. 每个节点的值都是 0。

🧭 我的初步思路

没想法

✅ 正确解法类型

树型dp

❗ 没想到的原因

题意与树型dp我无法将其匹配

📦 归入的题型分类

树型dp

🧠 触发词(以后遇到就联想)

二叉树安装摄像头->树型dp

🧪 解法一句话总结

分类讨论所有情况,从低向上讨论

代码如下:

 package main.java.class078;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code06_BinaryTreeCameras  
  * @description: 监控二叉树  
  * // 给定一个二叉树,我们在树的节点上安装摄像头  
  * // 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象  
  * // 计算监控树的所有节点所需的最小摄像头数量  
  * // 测试链接 : https://leetcode.cn/problems/binary-tree-cameras/  
  * @author: zs宝  
  * @create: 2025-09-20 10:23  
  * @Version 1.0  
  **/public class Code06_BinaryTreeCameras {  
     public class TreeNode {  
         int val;  
         TreeNode left;  
         TreeNode right;  
         TreeNode() {}  
         TreeNode(int val) { this.val = val; }  
         TreeNode(int val, TreeNode left, TreeNode right) {  
             this.val = val;  
             this.left = left;  
             this.right = right;  
         }  
     }  
     class Solution {  
         public static int ans;  
         public static int minCameraCover(TreeNode root) {  
             ans=0;  
             if(f(root)==0){  
                 ans++;  
             }  
             return ans;  
         }  
   
         //假设当前节点上方一定有父节点  
         //这里我们对摄像头当前节点状态做分类讨论  
         //0:当前节点没有被覆盖,但是子节点已经全部覆盖,  
         //1:当前节点被覆盖,子节点也被覆盖,摄像头未在当前节点上  
         //2:当前节点被覆盖了,子节点也被覆盖了,摄像头在当前节点上  
         public static int f(TreeNode x){  
             //3种分类讨论其实都是沿着叶子节点下的空节点慢慢网上推来的  
             //空节点该怎算呢,只能算覆盖呀,那么这样一来,  
             //在一个摄像头没加之前,每个叶子节点开始都是子节点被覆盖了,但是当前节点没有被覆盖--0状态  
             //然后由底到顶的推出1,2的可能性  
             if(x==null){  
                 return 1;  
             }  
             int left=f(x.left);  
             int right=f(x.right);  
             //分类讨论当前节点的情况  
             if(left==0 || right==0){  
                 ans++;  
                 return 2;  
             }  
             if(left==1 && right==1){  
                 return 0;  
             }  
             return 1;  
         }  
     }  
 }

7、路径总和Ⅲ

题目描述: 给定一个二叉树的根节点 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

解题思路:

  • 这个题主要在于题目说的是路径方向必须是向下的,要找到目标和,那就可以算出每个节点的前缀和preSUm,然后找到preSum-targetSum的前缀个数

  • 同时要注意回朔,否则可能出现其它枝丫的preSum-targetSum的前缀

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 437 + 路径总和Ⅲ

🧠 题目关键词(原文关键词)

树,路径,节点值之和等于 targetSum 的 路径 的数目,路径方向必须是向下

🧩 抽象问题类型(题目本质)

树型dp

🔍 数据规模 / 限制

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

🧭 我的初步思路

前缀和,回朔,树型dp

✅ 正确解法类型

前缀和,回朔,树型dp

❗ 没想到的原因

识别出来了

📦 归入的题型分类

树型dp

🧠 触发词(以后遇到就联想)

树的路径总和 targetSum 且必须向下->树型dp

🧪 解法一句话总结

算出每个节点的前缀和preSUm,然后找到preSum-targetSum的前缀个数,注意回朔

代码如下

 package main.java.class078;  
   
 import java.util.HashMap;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code07_PathSumIII  
  * @description: 路径总和 III  
  * // 给定一个二叉树的根节点 root ,和一个整数 targetSum  
  * // 求该二叉树里节点值之和等于 targetSum 的 路径 的数目  
  * // 路径 不需要从根节点开始,也不需要在叶子节点结束  
  * // 但是路径方向必须是向下的(只能从父节点到子节点)  
  * // 测试链接 : https://leetcode.cn/problems/path-sum-iii/  
  * @author: zs宝  
  * @create: 2025-09-20 11:05  
  * @Version 1.0  
  **/public class Code07_PathSumIII {  
     public class TreeNode {  
         int val;  
         TreeNode left;  
         TreeNode right;  
         TreeNode() {}  
         TreeNode(int val) { this.val = val; }  
         TreeNode(int val, TreeNode left, TreeNode right) {  
             this.val = val;  
             this.left = left;  
             this.right = right;  
         }  
     }  
     class Solution {  
         //树的前缀和如何表示  
         //前缀和的回朔,避免计算到不相干的两条分路上的枝丫  
         public static int ans;  
         public int pathSum(TreeNode root, int targetSum) {  
             HashMap<Long,Integer> map=new HashMap<>();  
             map.put(0L,1);  
             ans=0;  
             f(root,targetSum,0L,map);  
             return ans;  
         }  
   
         public static void f(TreeNode x, int targetSum,Long preSum,HashMap<Long,Integer>map){  
             if(x==null){  
                 return;  
             }  
             //当前节点前缀和  
             preSum+=x.val;  
             ans+=map.getOrDefault(preSum-targetSum,0);  
             map.put(preSum,map.getOrDefault(preSum,0)+1);  
             f(x.left,targetSum,preSum,map);  
             f(x.right,targetSum,preSum,map);  
             //回朔  
             map.put(preSum,map.get(preSum)-1);  
         }  
     }  
 }

参考资料