高频算法面试题(四十)- 二叉树中和为某一值的路径(一)

194 阅读1分钟

「这是我参与11月更文挑战的第 25 天,活动详情查看:2021最后一次更文挑战

刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能

二叉树中和为某一值的路径(一)

题目来源LeetCode-112. 路径总和

题目描述

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

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

示例

示例 1

1.png

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true

示例 2

2.png

输入:root = [1,2,3], targetSum = 5
输出:false

示例 3

输入:root = [1,2], targetSum = 0
输出:false

提示:

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

解题

解法一:递归

思路

在前边做过一道跟本题差不多的题,但那个比本题复杂,本题只需要判断有没有路径和为指定值的路径,并不要求获取路径

要求从根节点到叶子结点的路径和,是否等于指定值,是否就可以看成,根节点的子节点到叶子结点的路径和,是否等于targetSum-root.Val。这是不是就是一个递归的性质

然后你发现没有,我们需要计算每条路径的和,按照我们上边的思路,求当前结点到叶子结点的路径和,是先减去当前结点的值,再递归的求其子节点到叶子结点的路径和,想一想前序遍历,是不是可以解决这个问题,只不过在前序遍历的过程中,做了其它的操作和判断

所以说,二叉树的各种遍历十分重要,搞通透了,二叉树的题就好解,我之前对二叉树的各种遍历做了详细的总结,感兴趣的可以看这里

代码

//二叉树中和为某一值的路径(一)
//递归实现
func hasPathSum(root *TreeNode, targetSum int) bool {
	if root == nil {
		return false
	}

	if root.Left == nil && root.Right == nil && root.Val == targetSum { //如果当前结点是叶子结点,且叶子结点的值等于剩余的sum,说明存在路径和为指定值的路径
		return true
	}

	return hasPathSum(root.Left, targetSum - root.Val) || hasPathSum(root.Right, targetSum - root.Val) //左右子树存在任意一个即可
}

解法二:广度优先搜索

思路

前边已经出现了n道二叉树的题,用到了深度优先和广度优先。我们知道广度优先搜索的核心思想就是,先从最近的开始搜索,再搜索次近的。对应到二叉树上,就是逐层的搜索,也就是层序遍历

所以在做二叉树的时候,如果没有思路的时候,想想这些常应用在二叉树上的算法思想能否解决。我们知道用广度优先解决问题,通常要借助队列

我们可以定义一个队列,它里边存的是结点以及,从当前结点到叶节点的路径和的值(它这个值并不是真正的到叶节点的路径和的值,而是由targetSum - 它父节点的值得到的),这可能不好理解,看图(以例1的示例为例)

3.png 当某个节点是叶子结点,且它的值等于自身带的路径和的值时,说明存在路径和为targetSum的路径

代码

//广度优先搜索实现
type pair struct {
	node *TreeNode
	leftValue int
}
func hasPathSum2(root *TreeNode, targetSum int) bool {
	if root == nil {
		return false
	}

	queue := []pair{{root, targetSum}}
	for len(queue) != 0 {
		pairV := queue[0]
		if len(queue) > 1 {
			queue = queue[1:]
		} else {
			queue = []pair{}
		}

		node := pairV.node
		leftValue := pairV.leftValue
		if node.Left == nil && node.Right == nil {
			if leftValue == node.Val {
				return true
			}

			continue
		}
		if node.Left != nil {
			queue = append(queue, pair{node.Left, leftValue - node.Val})
		}
		if node.Right != nil {
			queue = append(queue, pair{node.Right, leftValue - node.Val})
		}
	}

	return false
}