【LeetCode】112. 路径总和
解题思路
要判断二叉树中是否存在根节点到叶子节点的路径和等于目标值 targetSum,可以通过 递归深度优先搜索(DFS) 或 迭代广度优先搜索(BFS) 实现。以下是两种方法的实现原理:
方法一:递归法(DFS)
核心思想:
从根节点开始,每次递归将 targetSum 减去当前节点的值。当到达叶子节点时,若剩余 targetSum 等于叶子节点的值,则存在满足条件的路径。
代码实现
type TreeNode struct { Val int Left *TreeNode Right *TreeNode } func hasPathSum(root *TreeNode, targetSum int) bool { if root == nil { return false // 空树直接返回false[3,5](@ref) } // 如果是叶子节点,检查当前值是否等于剩余targetSum if root.Left == nil && root.Right == nil { return root.Val == targetSum } // 递归检查左右子树,传递更新后的targetSum return hasPathSum(root.Left, targetSum - root.Val) || hasPathSum(root.Right, targetSum - root.Val) }
关键点解析:
- 空树处理:若根节点为空(如示例3),直接返回
false。 - 叶子节点判断:当左右子节点均为空时,检查当前节点值是否等于
targetSum。 - 递归传递:非叶子节点需递归左右子树,并将
targetSum减去当前节点的值。
复杂度分析:
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(h),递归栈深度(h为树高度,最坏情况为链状树,复杂度O(n))。
方法二:迭代法(BFS)
核心思想:
使用队列存储节点及其路径和。每次处理节点时,若为叶子节点且路径和等于 targetSum,则返回 true;否则将子节点及其累积和加入队列。
代码实现
func hasPathSum(root *TreeNode, targetSum int) bool { if root == nil { return false } // 队列存储节点及当前路径和 queue := []struct { Node *TreeNode Sum int }{ {root, root.Val}, } for len(queue) > 0 { current := queue[0] queue = queue[1:] node, sum := current.Node, current.Sum // 叶子节点且路径和等于目标值 if node.Left == nil && node.Right == nil && sum == targetSum { return true } // 非叶子节点:将子节点与更新后的路径和入队 if node.Left != nil { queue = append(queue, struct{Node *TreeNode; Sum int}{node.Left, sum + node.Left.Val}) } if node.Right != nil { queue = append(queue, struct{Node *TreeNode; Sum int}{node.Right, sum + node.Right.Val}) } } return false }
关键点解析:
- 队列初始化:根节点及其值作为初始状态入队。
- 路径和累积:每个子节点的路径和为父节点路径和加上当前节点值。
- 叶子节点检查:出队时检查是否为叶子节点且满足条件。
复杂度分析:
- 时间复杂度:O(n),每个节点访问一次。
- 空间复杂度:O(n),队列存储最坏情况下的所有节点。
示例验证
func main() { // 示例1:输入 root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 root1 := &TreeNode{Val:5, Left: &TreeNode{Val:4, Left: &TreeNode{Val:11, Left: &TreeNode{Val:7}, Right: &TreeNode{Val:2}}}, Right: &TreeNode{Val:8, Left: &TreeNode{Val:13}, Right: &TreeNode{Val:4, Right: &TreeNode{Val:1}}}, } fmt.Println(hasPathSum(root1, 22)) // 输出: true // 示例2:输入 root = [1,2,3], targetSum = 5 root2 := &TreeNode{Val:1, Left: &TreeNode{Val:2}, Right: &TreeNode{Val:3}} fmt.Println(hasPathSum(root2, 5)) // 输出: false }
边界条件与注意事项
- 空树处理:根节点为空时无论
targetSum是否为0,均返回false(如示例3)。 - 负数节点:路径中间和可能为负数,因此必须遍历到叶子节点才能终止递归。
- 单个节点:若树仅有一个节点(根节点即叶子节点),直接判断其值是否等于
targetSum。
总结
- 递归法:代码简洁,优先推荐使用,适合树较平衡的场景。
- 迭代法:显式控制遍历过程,适合深度较大的树或避免栈溢出的场景。
两种方法均能高效解决问题,选择时可根据具体场景权衡代码简洁性与内存消耗。

浙公网安备 33010602011771号