记录 1 道算法题
路径总和
要求:
* 从根节点到叶子节点的某一条路径
* val的总和等于给定的数则为正确路径,反之错误
这道题有两种解法,一个是广度,一个是深度。
无论是哪种都要先做是否叶子节点的判断,不是叶子节点即使数对了也算错。
广度是指把所有节点都做收集的动作,根据累加的方法,比较在某个节点时累加的数是不是等于给定的数。
1
2 7
4 3 5
给定一个数 6
当收集到 1 的节点的时候,累计是1,不是叶子节点
然后继续到 2 节点的时候, 累计是上面的 1 加上自己的val 2,同样不是叶子节点
然后继续到 7 节点, 累计是 上面的 1 加上自己的val 7,不是叶子节点
继续到 4 节点的时候, 累计是上面的 3 加自己的 4,是叶子节点。 7 !== 6, 所以不对
然后到 3 节点, 累计是 上面的 3 加上自己的 3, 是叶子节点 等于6,所以是这个路径是对的,返回。
可以看出, 广度是以节点为单位,一层层收集。知道某个节点的时候也就知道了目前累计到多少,然后传给他的 left right, 这样他的子节点也知道了目前累计到多少。
就是斐波那契数列的既视感。
function hasPathSum(root, targetSum) {
if (!root) return false
// 准备数组 存放收集起来的节点,因为一个节点能够算出他的left和right
// 所以肯定是收集的速度比检查的速度快
// 因为不好处理第一个,所以先写死。
const stack = [root]
const values = [root.val]
// 如果用数组的 shift 的话,因为新的在后面推入了,所以只能 shift
// while(stack.length) {
// const node = stack.shift()
// const val = values.shift()
// }
// 不用shift的版本就是通过下标控制
while(i < stack.length) {
const node = stack[i]
const val = values[i]
i++
// 叶子节点
if (!node.left && !node.right) {
if (val === targetSum) {
return true
} else {
// 是叶子节点,但不符合给定的数
continue
}
}
if(node.left) {
stack.push(node.left)
// val 是已经累加到当前节点的数
values.push(node.left.val + val)
}
if (node.right) {
stack.push(node.right)
values.push(node.right.val + val)
}
}
return false
}
深度是跟前序遍历一样的,先把某一条路走完,判断合不合理,然后不停回溯,走新路。直到找到或者没找到。用的是递减的方法。
说到前序遍历就是递归。
function hasPathSum(root, targetSum) {
if (!root) return false
// 先计算当前节点是否符合
targetSum -= root.val
// 叶子节点时进行比较
if (!root.left && !root.right) {
return targetSum === 0
}
// 在一开始已经减过了。
// 同一个父节点,所以 累计的数是一样的。
// 用了个或,刚刚好如果前面返回了true就返回了,false就等于回溯然后走新路。跟前序遍历很像的。
return hasPathSum(root.left, targetSum) ||
hasPathSum(root.right, targetSum)
}