面试经典算法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
思路
递归
- 定义一个递归函数
hasPathSum
,接收一个节点指针node
和一个整数target
作为参数,表示从根节点到当前节点路径上的节点值之和等于target
。 - 如果
node
为空,直接返回false
,表示不存在这样的路径。 - 如果
node
是叶子节点(即左右子节点都为空),判断target
是否等于node
的值,如果是则返回true
,否则返回false
。 - 如果
node
不是叶子节点,递归地判断其左子节点和右子节点是否存在符合条件的路径,只要有一条路径存在,就返回true
。
非递归
- 两个队列分别存储节点指针和对应的目标和。
- 将根节点和目标和入队。
- 循环直到队列为空:
- 弹出当前节点和当前目标和。
- 如果当前节点是叶子节点,并且当前目标和等于叶子节点的值,则返回
true
。 - 如果当前节点不是叶子节点,将其子节点和当前目标和减去当前节点值分别入队。
图解
- 申请两个队列分别存储节点指针和对应的目标和,并将根节点、目标和减去根节点值入队。
- 如果当前存储节点的队列不为空时,先计算当前层级的节点个数,进入循环,弹出当前节点和当前目标和。
- 如果当前节点有左右子树,将节点及目标和减去左右子节点值入队存入队列。
- 继续循环,弹出当前节点和当前目标和。
- 将当前节点的左右节点和对应的目标和入队。
- 弹出当前节点和当前目标和。
- 将当前节点的左右节点和对应的目标和入队。
- 弹出当前节点和当前目标和。
- 将当前节点的左右节点和对应的目标和入队。
- 弹出当前节点和当前目标和。
当前节点是叶子节点,但是目标和不为0,返回false。
- 继续弹出当前节点和当前目标和。
- 将当前节点的左右节点和对应的目标和入队。
- 弹出当前节点和当前目标和。
当前节点是叶子节点,但是目标和不为0,返回false。
- 继续弹出当前节点和当前目标和。
当前节点是叶子节点,目标和为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) # 输出结果