路径总和系列

本文详细介绍了三种不同的二叉树路径求和问题的解决方案,包括路径总和I、路径总和II和路径总和III。针对每种问题,提供了深度优先遍历的递归策略,通过回溯找到满足条件的路径。在路径总和I中,找到一条路径使其节点值之和等于目标和;路径总和II则需找出所有这样的路径;路径总和III计算满足特定条件的路径数量。
摘要由CSDN通过智能技术生成

路径总和 I

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。

叶子节点 是指没有子节点的节点。

示例:
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true

思路

使用深度优先遍历的方式来遍历二叉树

  1. 确定递归函数的参数和返回类型
    参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
    再来看返回值,递归函数什么时候需要返回值,什么时候不需要返回值?
    如果需要搜索整棵二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。
    而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回。

  2. 确定终止条件
    计数器如何统计这一条路径的和呢?
    用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
    如果最后count == 0,同时到了叶子节点的话(注意,必须走完,走到叶子节点),说明找到了目标和。
    如果遍历到了叶子节点,count不为0,就是没找到。
    递归终止条件代码如下:

if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0。
if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找到合适的边,直接返回。
  1. 确定单层递归的逻辑
    因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。
    递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,就应该立刻返回。
    代码如下:
if (cur->left) {
	// 遇到叶子节点返回true,则直接返回true
	if (traversal(cur->left, count - cur -> left -> val)) return true; // 注意这里有回溯的逻辑
}
if (cur->right) {
	// 遇到叶子节点返回true,则直接返回true
	if (traversal(cur->right, count - cur->right->val)) return true; // 回溯
}
return false

以上代码中是包含着回溯的,没有回溯,如何后撤重新找另一条路径呢?
回溯隐藏在traversal(cur->left, count - cur -> left -> val)这里,因为把count - cur -> left -> val直接作为参数传进去,函数结束,count的数值没有改变。
整体代码如下:

class Solution {
private:
    bool traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
        if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回

        if (cur->left) { // 左
            count -= cur->left->val; // 递归,处理节点;
            if (traversal(cur->left, count)) return true;
            count += cur->left->val; // 回溯,撤销处理结果
        }
        if (cur->right) { // 右
            count -= cur->right->val; // 递归,处理节点;
            if (traversal(cur->right, count)) return true;
            count += cur->right->val; // 回溯,撤销处理结果
        }
        return false;
    }

public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == NULL) return false;
        return traversal(root, sum - root->val);
    }
};

路径总和 II

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

叶子节点 是指没有子节点的节点。

示例
在这里插入图片描述
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]

思路

要遍历整个树,找到所有路径,递归函数不要返回值!
在这里插入图片描述

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    // 递归函数不需要返回值,因为我们要遍历整个树
    void traversal(TreeNode* cur, int count) {
        if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点切找到了和为sum的路径
            result.push_back(path);
            return;
        }

        if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回

        if (cur->left) { // 左 (空节点不遍历)
            path.push_back(cur->left->val);
            count -= cur->left->val;
            traversal(cur->left, count);    // 递归
            count += cur->left->val;        // 回溯
            path.pop_back();                // 回溯
        }
        if (cur->right) { // 右 (空节点不遍历)
            path.push_back(cur->right->val);
            count -= cur->right->val;
            traversal(cur->right, count);   // 递归
            count += cur->right->val;       // 回溯
            path.pop_back();                // 回溯
        }
        return ;
    }

public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        result.clear();
        path.clear();
        if (root == NULL) return result;
        path.push_back(root->val); // 把根节点放进路径
        traversal(root, sum - root->val);
        return result;
    }
};

路径总和III

给定一个二叉树,它的每个节点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点还是,也不需要在叶子节点结束,但是路径方向必须是向下的(父节点到子节点)。

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/
5 -3
/ \
3 2 11
/ \
3 -2 1
返回 3。和等于 8 的路径有:

  1. 5 -> 3
  2. 5 -> 2 -> 1
  3. -3 -> 11

思路:双重DFS

注意,DFS是DFS,回溯是回溯。回溯要有撤销处理的操作,但是DFS可以没有!

  1. 先序递归遍历每个节点
  2. 以每个节点作为起始节点DFS寻找满足条件的路径
class Solution {
public: 
	int ans = 0;
	void dfs(TreeNode* root, int sum)
	{
		if (root == nullptr)
			return;
		sum -= root->val;
		if (sum == 0)
			ans++;
		dfs(root -> left, sum);
		dfs(root -> right, sum);
	}
	int pathSum(TreeNode* root, int sum)
	{
		if (root == nullptr)
			return ans;
		dfs(root, sum);
		pathSum(root->left, sum);
		pathSum(root->right, sum);
		return ans;
	}
};

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值