Skip to content

路径总和 ​

LeetCode 112

问题描述 ​

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

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

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000] 内
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

思路 ​

递归 ​

  1. 定义一个递归函数 hasPathSum,接收一个节点指针 node 和一个整数 target 作为参数,表示从根节点到当前节点路径上的节点值之和等于 target。
  2. 如果 node 为空,直接返回 false,表示不存在这样的路径。
  3. 如果 node 是叶子节点(即左右子节点都为空),判断 target 是否等于 node 的值,如果是则返回 true,否则返回 false。
  4. 如果 node 不是叶子节点,递归地判断其左子节点和右子节点是否存在符合条件的路径,只要有一条路径存在,就返回 true。

非递归 ​

  1. 两个队列分别存储节点指针和对应的目标和。
  2. 将根节点和目标和入队。
  3. 循环直到队列为空:
    • 弹出当前节点和当前目标和。
    • 如果当前节点是叶子节点,并且当前目标和等于叶子节点的值,则返回 true。
    • 如果当前节点不是叶子节点,将其子节点和当前目标和减去当前节点值分别入队。

图解 ​

  1. 申请两个队列分别存储节点指针和对应的目标和,并将根节点、目标和减去根节点值入队。

  1. 如果当前存储节点的队列不为空时,先计算当前层级的节点个数,进入循环,弹出当前节点和当前目标和。

  1. 如果当前节点有左右子树,将节点及目标和减去左右子节点值入队存入队列。

  1. 继续循环,弹出当前节点和当前目标和。

  1. 将当前节点的左右节点和对应的目标和入队。

  1. 弹出当前节点和当前目标和。

  1. 将当前节点的左右节点和对应的目标和入队。

  1. 弹出当前节点和当前目标和。

  1. 将当前节点的左右节点和对应的目标和入队。

  1. 弹出当前节点和当前目标和。

当前节点是叶子节点,但是目标和不为0,返回false。

  1. 继续弹出当前节点和当前目标和。

  1. 将当前节点的左右节点和对应的目标和入队。

  1. 弹出当前节点和当前目标和。

当前节点是叶子节点,但是目标和不为0,返回false。

  1. 继续弹出当前节点和当前目标和。

当前节点是叶子节点,目标和为0,返回true。

参考代码 ​

C++ ​

递归 ​
cpp
#include <iostream>

using namespace std;

// 二叉树节点的定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        // 如果根节点为空,直接返回false
        if (!root) {
            return false;
        }
        // 如果是叶子节点,判断当前节点的值是否等于目标和
        if (!root->left && !root->right) {
            return targetSum == root->val;
        }
        // 递归判断左子树和右子树是否存在符合条件的路径
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};

// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {
    if (index >= nodes.size() || nodes[index] == -1) {
        return nullptr; // 如果节点为空,则返回nullptr
    }
    TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点
    root->left = createTree(nodes, 2 * index + 1); // 创建左子树
    root->right = createTree(nodes, 2 * index + 2); // 创建右子树
    return root; // 返回当前节点
}

int main() {
    vector<int> nodes = {5, 4, 8, 11, -1, 13, 4, 7, 2, -1, -1, -1, 1}; // 二叉树的先序遍历序列
    TreeNode* root = createTree(nodes, 0); // 创建二叉树
    int targetSum = 22;
    Solution solution;
    bool result = solution.hasPathSum(root, targetSum); // 判断是否存在根节点到叶子节点路径的节点值之和等于目标和
    cout << (result ? "true" : "false") << endl; // 输出结果

    return 0;
}
非递归 ​
cpp
#include <iostream>
#include <queue>

using namespace std;

// 二叉树节点的定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (!root) {
            return false; // 如果根节点为空,直接返回false
        }
        queue<TreeNode*> nodeQueue; // 存储节点指针的队列
        queue<int> sumQueue; // 存储目标和减去当前路径节点值之和的队列
        nodeQueue.push(root); // 根节点入队
        sumQueue.push(targetSum - root->val); // 目标和减去根节点值入队

        while (!nodeQueue.empty()) {
            int size = nodeQueue.size(); // 当前层级的节点数
            for (int i = 0; i < size; i++) {
                TreeNode* currNode = nodeQueue.front(); // 弹出当前节点
                nodeQueue.pop();
                int currSum = sumQueue.front(); // 弹出当前目标和
                sumQueue.pop();

                if (!currNode->left && !currNode->right && currSum == 0) {
                    return true; // 如果当前节点是叶子节点,并且当前目标和为0,返回true
                }

                if (currNode->left) {
                    nodeQueue.push(currNode->left); // 左子节点入队
                    sumQueue.push(currSum - currNode->left->val); // 目标和减去左子节点值入队
                }
                if (currNode->right) {
                    nodeQueue.push(currNode->right); // 右子节点入队
                    sumQueue.push(currSum - currNode->right->val); // 目标和减去右子节点值入队
                }
            }
        }

        return false; // 遍历完所有节点都没有找到符合条件的路径,返回false
    }
};

// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {
    if (index >= nodes.size() || nodes[index] == -1) {
        return nullptr; // 如果节点为空,则返回nullptr
    }
    TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点
    root->left = createTree(nodes, 2 * index + 1); // 创建左子树
    root->right = createTree(nodes, 2 * index + 2); // 创建右子树
    return root; // 返回当前节点
}

int main() {
    vector<int> nodes = {5, 4, 8, 11, -1, 13, 4, 7, 2, -1, -1, -1, 1}; // 二叉树的先序遍历序列
    TreeNode* root = createTree(nodes, 0); // 创建二叉树
    int targetSum = 22;
    Solution solution;
    bool result = solution.hasPathSum(root, targetSum); // 判断是否存在根节点到叶子节点路径的节点值之和等于目标和
    cout << (result ? "true" : "false") << endl; // 输出结果

    return 0;
}

Java ​

java
import java.util.LinkedList;
import java.util.Queue;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }
}

public class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false; // 如果根节点为空,直接返回false
        }

        Queue<TreeNode> nodeQueue = new LinkedList<>(); // 存储节点指针的队列
        Queue<Integer> sumQueue = new LinkedList<>(); // 存储目标和减去当前路径节点值之和的队列
        nodeQueue.offer(root); // 根节点入队
        sumQueue.offer(targetSum - root.val); // 目标和减去根节点值入队

        while (!nodeQueue.isEmpty()) {
            int size = nodeQueue.size(); // 当前层级的节点数
            for (int i = 0; i < size; i++) {
                TreeNode currNode = nodeQueue.poll(); // 弹出当前节点
                int currSum = sumQueue.poll(); // 弹出当前目标和

                if (currNode.left == null && currNode.right == null && currSum == 0) {
                    return true; // 如果当前节点是叶子节点,并且当前目标和为0,返回true
                }

                if (currNode.left != null) {
                    nodeQueue.offer(currNode.left); // 左子节点入队
                    sumQueue.offer(currSum - currNode.left.val); // 目标和减去左子节点值入队
                }
                if (currNode.right != null) {
                    nodeQueue.offer(currNode.right); // 右子节点入队
                    sumQueue.offer(currSum - currNode.right.val); // 目标和减去右子节点值入队
                }
            }
        }

        return false; // 遍历完所有节点都没有找到符合条件的路径,返回false
    }

    // 创建二叉树
    public static TreeNode createTree(Integer[] nodes, int index) {
        if (index >= nodes.length || nodes[index] == null) {
            return null; // 如果节点为空,则返回null
        }
        TreeNode root = new TreeNode(nodes[index]); // 创建当前节点
        root.left = createTree(nodes, 2 * index + 1); // 创建左子树
        root.right = createTree(nodes, 2 * index + 2); // 创建右子树
        return root; // 返回当前节点
    }

    public static void main(String[] args) {
        Integer[] nodes = {5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1}; // 二叉树的先序遍历序列
        TreeNode root = createTree(nodes, 0); // 创建二叉树
        int targetSum = 22;
        Solution solution = new Solution();
        boolean result = solution.hasPathSum(root, targetSum); // 判断是否存在根节点到叶子节点路径的节点值之和等于目标和
        System.out.println(result); // 输出结果
    }
}

Python ​

python
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
        if not root:
            return False

        node_queue = [root]  # 存储节点的队列
        sum_queue = [targetSum - root.val]  # 存储目标和减去当前路径节点值之和的队列

        while node_queue:
            curr_node = node_queue.pop(0)  # 弹出当前节点
            curr_sum = sum_queue.pop(0)  # 弹出当前目标和

            if not curr_node.left and not curr_node.right and curr_sum == 0:
                return True  # 如果当前节点是叶子节点,并且当前目标和为0,返回True

            if curr_node.left:
                node_queue.append(curr_node.left)  # 左子节点入队
                sum_queue.append(curr_sum - curr_node.left.val)  # 目标和减去左子节点值入队
            if curr_node.right:
                node_queue.append(curr_node.right)  # 右子节点入队
                sum_queue.append(curr_sum - curr_node.right.val)  # 目标和减去右子节点值入队

        return False  # 遍历完所有节点都没有找到符合条件的路径,返回False

# 创建二叉树
def create_tree(nodes, index):
    if index >= len(nodes) or nodes[index] is None:
        return None  # 如果节点为空,则返回None
    root = TreeNode(nodes[index])  # 创建当前节点
    root.left = create_tree(nodes, 2 * index + 1)  # 创建左子树
    root.right = create_tree(nodes, 2 * index + 2)  # 创建右子树
    return root  # 返回当前节点

nodes = [5, 4, 8, 11, None, 13, 4, 7, 2, None, None, None, 1]  # 二叉树的先序遍历序列
root = create_tree(nodes, 0)  # 创建二叉树
target_sum = 22
solution = Solution()
result = solution.hasPathSum(root, target_sum)  # 判断是否存在根节点到叶子节点路径的节点值之和等于目标和
print(result)  # 输出结果