【算法专题训练】31、二叉树路径和

1、LCR 049. 求根节点到叶节点数字之和

题目信息:

  • https://leetcode.cn/problems/3Etpl5/description/
给定一个二叉树的根节点 root ,树中每个节点都存放有一个 09 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。

示例 1:
输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25

示例 2:
输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

提示:
树中节点的数目在范围 [1, 1000]0 <= Node.val <= 9
树的深度不超过 10

解题思路:

  • 1、审题:输入一棵二叉树,二叉树中结点值的数据范围为0到9的数字,现在要求找到从根节点到叶子结点组合成的数字,并将這些数字全部相加
  • 2、解题:
  • 使用前序遍历,将从根节点到叶子结点的组成的数字全部返回,组成数字的规则,是在之前的数字基础上乘以10再加上当前遍历的节点值
  • 如果遍历到了空节点则返回0,如果遍历到了叶子结点则返回当前遍历到的数据,如果还存在左右子节点,则继续前序遍历,返回左右子树的组合数字之和

代码实现:

int dfs(TreeNode *node, int num)
{
    if (node == nullptr)
    {
        return 0;
    }
    // 到当前结点的路径和
    int path = num * 10 + node->val;
    // 叶子结点
    if (node->left == nullptr && node->right == nullptr)
    {
        return path;
    }

    // 还存在左右子树
    return dfs(node->left, path) + dfs(node->right, path);
}

int sumNumbers(TreeNode *root)
{
    return dfs(root, 0);
}

2、LCR 050. 路径总和 III

题目信息:

  • https://leetcode.cn/problems/6eUYwP/description/
给定一个二叉树的根节点 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

解题思路:

  • 1、审题:输入一棵二叉树和一个目标值targetSum,要求找到在二叉树中连续节点的值累加和等于目标值的个数
  • 2、解题:
  • 题目要求在二叉树中连续的结点路径,并没有要求一定要从根节点开始,到叶子结点结束,中间部分的结点路径之和也可以
  • 使用前序遍历法,记录遍历到当前结点的路径之和path,
  • 使用map保存遍历过程中路径之和和对应个数,这样做的目的可以通过当前路径之和path减去目标值targetSum,判断map中是否存在该差值的键,
    • 如果存在则说明到当前结点位置结束,存在一条路径(可能是中间某个节点开始),其路径之和等于目标值
    • 相当于在遍历过程中,遍历到每个节点都有一个路径之和path,且都有一个对应的map集合,键值对为路径之和与路径和对应个数的值
  • 通过判断路径之和与目标值targetSum之差,来判断结果值是否在map集合中是否存在对应的键
  • 因为前序遍历,从根节点开始遍历到左子树再到右子树,path的值是有副本,但是map的值是同一个指针,所以在遍历完当前结点后,需要将到该结点的路径和减去其个数表示退出当前结点
  • 在前序遍历的过程中,将所求问题进行拆解,最终的问题是求输入的目标二叉树中路径和等于目标值的个数,可以拆解为三个部分,
    • 也就是到当前结点位置的个数,和到左右子树的个数,三者之和

代码实现:

int dfs(TreeNode *node, int target, std::map<double, int> map, double path)
{
  if (node == nullptr)
  {
    return 0;
  }
  // 到当前结点时,他的路径之和
  path = path + node->val;
  // 求到当前结点他的路径和的个数
  int count = 0;
  if (map.find(path - target) != map.end()) // 存在这样的路径和
  {
    count = map[path - target];
  }
  // 添加path路径和的个数
  if (map.find(path) != map.end()) // 存在这样的路径和
  {
    int tempC = map[path];
    map[path] = tempC + 1;
  }
  else
  {
    map[path] = 1;
  }

  // 加上到左右子树位置时,他的路径之和
  count += dfs(node->left, target, map, path);
  count += dfs(node->right, target, map, path);

  // 这个节点遍历结束了,则到这个节点的路径和,需要在map中删除
  int tempC = map[path];
  map[path] = tempC - 1;
  return count;
}

int pathSum(TreeNode *root, int targetSum)
{
  std::map<double, int> map;
  map[0] = 1;
  return dfs(root, targetSum, map, 0);
}

3、LCR 051. 二叉树中的最大路径和

题目信息:

  • https://leetcode.cn/problems/jC7MId/description/
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。
该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。

给定一个二叉树的根节点 root ,返回其 最大路径和,即所有路径上节点值之和的最大值。

示例 1:
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2:
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:
树中节点数目范围是 [1, 3 * 104]

解题思路:

  • 1、审题:输入一棵非空的二叉树,要求在这棵二叉树中,查找一条路径上的结点,要求该路径上的所有节点值和最大,并返回这个最大值
  • 2、解题:
  • 题目要求的该路径没有说要从根节点开始,也没有说要到某个节点结束,可以是树中任意个结点组合成的路径
  • 问题的核心为如何找到这个路径,使用后续遍历方式,遍历这棵二叉树,
  • 将问题进行细化,可以看做左子树,根节点和右子树三个部分,那这条路径可以有好几种情况:
    • 左子树的最大值路径; 右子树的最大值路径; 左子树+根节点+右子树组合成的路径;
    • 而在遍历时返回的值,是当前左子树或者左子树经过当前结点组成的路径和的最大值
  • 所以每次遍历左右子树的时候,都是传入一个对象引用,

代码实现:

int dfs(TreeNode *node, int &maxSum)
{
  if (node == nullptr)
  {
    return 0; // 叶子结点
  }

  // 非叶子结点,获取当前结点下的左子树的最大值
  int leftMaxSum = INT32_MIN;
  // 如果左子树路径上的结点全是负数,则不取这个路径上的任何一个节点
  int left = max(0, dfs(node->left, leftMaxSum));

  // 右子树也一样
  int rightMaxSum = INT32_MIN;
  int right = max(0, dfs(node->right, rightMaxSum));

  // 左右子树的最大值,不包含根节点
  maxSum = max(leftMaxSum, rightMaxSum);

  // 求最大路径和,全路径
  maxSum = max(maxSum, left + right + node->val);

  // 返回左或右子树的最大值路径
  return node->val + max(left, right);
}

int maxPathSum(TreeNode *root)
{
  int maxSum = INT32_MIN;
  dfs(root, maxSum);
  return maxSum;
}

4、总结

  • 二叉树的路径和,是对二叉树进行前序遍历,从根节点开始不断遍历到叶子结点的这一条路径所表示的数字之和,分为左右子树,也就是两个路径之和,直到叶子结点返回0.
  • 二叉树的一段路径之和,在遍历的过程中既要将路径上的节点值累加,也要记录这个路径和的个数,使用map结构保存路径和与对应的个数
  • 二叉树的最大路径和,该路径不要方向,从另一个节点到相邻节点组成的路径和为目标路径和,通过递归的方式,把题目要求缩小来解决。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值