面试经典算法系列之二叉树15 - 路径总和

面试经典算法30 - 路径总和

LeetCode.112
公众号:阿Q技术站

问题描述

给你二叉树的根节点 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++
递归
#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;
}
非递归
#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
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
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)  # 输出结果
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值