Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
是否存在从当前节点 root 到叶子节点的路径,满足其路径和为 sum
let root = {
value: 5,
left: {
value: 4,
left: {
value: 11,
left: {
value: 7,
left: null,
right: null
},
right: {
value: 2,
left: null,
right: null
}
},
right: null
},
right: {
value: 8,
left: {
value: 13,
left: null,
right: null
},
right: {
value: 4,
left: null,
right: {
value: 1,
left: null,
right: null
}
}
}
}
当 target=22 时,路径为 5,4,11,2
二、思路分析:
题目给我们的是一个二叉树,我们需要想办法遍历二叉树,关于遍历二叉树有有3种方式:
- 中序遍历按照节点上的键值,以升序访问二叉树上的所有节点。
- 先序遍历先访问根节点,然后以同样方式访问左子树和右子树。
- 后序遍历先访问叶子节点,从左子树到右子树,再到根节点。
根据题目意思:是否存在从当前节点 root 到叶子节点的路径,满足其路径和为 sum。我们需要使用先序遍历来处理,先处理根节点,再处理叶子节点。
// 深度优先
function hasPathSum (root, sum) {
if (root == null) {
return false
}
if (root.left == null && root.right == null) {
return root.value === sum
}
return hasPathSum(root.left, sum - root.value) || hasPathSum(root.right, sum - root.value)
}
对于一颗二叉树,深度优先搜索(Depth First Search)(其实是二叉树的先序遍历)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
关于二叉树的遍历除了使用递归我们还可以使用队列来模拟:
// 广度优先搜索
function hasPathSum (root, sum) {
if (root === null) {
return false
}
let node = [root]
let value = [root.value]
while (node.length) {
let now = node.shift()
let temp = value.shift()
if (temp === sum) {
return true
}
if (now.left && now.left !== null) {
node.push(now.left)
value.push(now.left.value + temp)
}
if (now.right && now.right !== null) {
node.push(now.right)
value.push(now.right.value + temp)
}
}
return false
}
广度优先搜索(Breadth First Search)(其实是二叉树的层次遍历),又叫宽度优先搜索或横向优先搜索,是从根结点开始沿着树的宽度搜索遍历,上面二叉树的遍历顺序为:5,4,8,11,13,4,7,2,1 可以利用队列实现广度优先搜索。
我们再看一个面试题,计算二叉树的所有路径。
function binaryTree (root) {
let paths = []
let construct_paths = (root, path) => {
if (root) {
path += root.value.toString()
if (root.left === null && root.right === null) {
paths.push(path)
} else {
// 当前节点不是叶子节点,继续递归
path += '->'
construct_paths(root.left, path)
construct_paths(root.right, path)
}
}
}
construct_paths(root, '')
return paths
}
// [ '5->4->11->7', '5->4->11->2', '5->8->13', '5->8->4->1' ]
我们继续使用广度优先搜索重写算法:
function binaryTree (root) {
if (root === null) {
return false
}
let node = [root]
let value = [root.value]
let paths = []
while (node.length) {
let now = node.shift()
let temp = value.shift()
if (now.left === null && now.right === null) {
paths.push(temp)
}
if (now.left && now.left !== null) {
node.push(now.left)
value.push(temp + '->'+ now.left.value)
}
if (now.right && now.right !== null) {
node.push(now.right)
value.push(temp + '->'+ now.right.value)
}
}
return paths
}
这里在提一句,深度优先和广度优先的感念, “深度优先搜索就是树的先序遍历”,“广度优先搜索就是树的按层遍历”。