开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
引言
算法的技能对于程序员是百益而无一害,作为程序员无论是前端还是后端算法技能对于我们都是十分十分的重要,我将陆续整理并讲解前端程序员必须掌握的经典算法。
题目描述
给你二叉树的根节点 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
解释: 由于树是空的,所以不存在根节点到叶子节点的路径。
分析
根据题目的分析,我们需要明白如何保存每个根节点到叶子节点的路径?此处我们可以巧妙地通过并查集数据结构完成设计,然后我们通过队列在进行层序遍历地同时保存每个节点地父亲,最后进行根节点统计判断和路径
- 初始化一个队列和保存所有节点父亲地map数据结构
- 基于二叉树地层序遍历设计思想,遍历节点的同时将节点父亲保存
- 收集所有的叶子节点,最后遍历叶子节点。
- 通过并查集数据结构判断每个叶子到根的路径和是否满足目标条件。
解答
var hasPathSum = function(root, targetSum) {
let que = []
let map = new Map()
let rear = []
if(!root) return false
que.push(root)
map.size(root,null)
while(que.length) {
let len = que.length
for(let i=0;i<len;i++) {
let node = que.shift()
if(node.left) {
map.set(node.left,node)
que.push(node.left)
}
if(node.right) {
map.set(node.right,node)
que.push(node.right)
}
if(!node.left && !node.right) rear.push(node)
}
}
//寻找是否满足
for(let i=0;i<rear.length;i++) {
let node = rear[i]
let sum = 0
while(node) {
sum += node.val
node = m.get(node)
}
if(sum===targetSum) return true
}
return false
}
整体的设计是十分清理的,通过队列配合并查集的设计解决了问题,需要灵活的配合各种数据结构解决。
总结
通过对路径总和的讲解,大家学到了进行问题的解决。通过队列配合并查集数据结构巧妙计算可以优雅的在低时间和空间复杂度下完成任务。