257. 二叉树的所有路径 - 力扣(LeetCode)
文章起笔:2021年11月13日22:22:02
问题描述及示例
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]
示例 2:
输入:root = [1]
输出:[“1”]
注意:
树中节点的数目在范围 [1, 100] 内
-100 <= Node.val <= 100
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解
我的题解1(DFS;前序遍历)
之前做过一道题目:
虽然上面这道题和本题要解决的问题不一样,但是解决问题的原理都是一样的。上面那道题其实也是先要求二叉树的所有路径。
所以,在上面那道题的基础上,我做了一些改进和增减操作,写出了下面的这种DFS解法:
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {string[]}
*/
var binaryTreePaths = function (root) {
// paths用于存储所有找到的路径
let paths = [];
dfs(root, []);
return paths;
// dfs用于获取以node为根节点的树的所有路径,
// 其中,path用于存储某一条路径,其初始值为一个空数组
function dfs(node, path) {
// 如果当前节点为空节点,则用return语句结束当前层的递归
if (!node) {
return;
}
// 否则的话就在path数组中存入当前遍历的节点的节点值
path.push(node.val);
// 如果当前节点为叶子节点
if (!node.left && !node.right) {
// 说明找到了一条完整路径,此时将path中存储的完整路径用 -> 拼接起来并压入paths中
paths.push(path.join('->'));
}
// 然后继续向下遍历,注意,此处使用了扩展运算符将目前找到的路径作为参数传递给了下一层
// 这里的传参操作是整个函数的核心
dfs(node.left, [...path]);
dfs(node.right, [...path]);
}
};
提交记录
执行结果:通过
208 / 208 个通过测试用例
执行用时:72 ms, 在所有 JavaScript 提交中击败了73.25%的用户
内存消耗:39 MB, 在所有 JavaScript 提交中击败了94.85%的用户
时间:2021/11/13 22:25
我的题解2(BFS)
其实,这种找路径的题目用DFS来做会比较合适,因为寻找一条路径的过程就是一个深度优先搜索的过程。
但是其实也可以使用BFS的思路,因为说到底,本题还是在遍历一棵二叉树嘛,而我们使用BFS其实也是可以达到遍历的目的的。有关BFS遍历的描述,详细可参看下方链接:
参考:【算法-LeetCode】102. 二叉树的层序遍历(二叉树;层序遍历;BFS;生成二叉树)_赖念安的博客-优快云博客
本题中运用到的基本思路和上面的这个博客里的思路是一样的。唯一需要特别注意的就是,我们不是单纯地利用某一层的遍历结果,而是把上一层的遍历结果也保留到当前层中,这种保存之前遍历结果的操作就像是上面的DFS中关键的那个向下一层递归传参的操作一样。
var binaryTreePaths = function (root) {
let paths = [];
// pathQueue是一个用于保存路径的队列,在形式上是一个二维数组,
// 其中的每一个元素都是一条路径,但是注意该路径并不一定都完整(即并不一定都包含叶子节点)
// 理解这点是非常关键的,因为下面针对这个队列的操作的原由就是由此推出的
let pathQueue = [];
// 注意pathQueue的初始值是一条包含根节点的路径
pathQueue.push([root.val]);
// nodeQueue用于存储遍历过程中的节点,这点和普通的层序遍历中借助的队列结构一样
let nodeQueue = [root];
// 下面层序遍历的逻辑和普通的层序遍历是一样的,该注意的点也在上面的博客中提到了
// 关键是要关注我们对pathQueue这个队列的操作,其实一开始,我们对pathQueue的操作
// 大体上和对nodeQueue的操作是一样的
while (nodeQueue.length) {
for (let i = 0, len = nodeQueue.length; i < len; i++) {
let node = nodeQueue.shift();
let path = pathQueue.shift();
// 如果当前节点是叶子节点,则说明找到一条完整的路径path,需要将其存入paths中
if (!node.left && !node.right) {
paths.push(path.join('->'));
continue;
}
// 如果当前节点的左子节点不为空
if (node.left) {
// 则将其压入nodeQueue队列,这里和普通的层序遍历是一样的
nodeQueue.push(node.left);
// 接下来的操作就和普通的层序遍历有点不一样了
// 拷贝一份path的值并用 temp 变量接收,因为path是数组对象,
// 所以不能简单地写成 temp = path,否则会影响后续操作
let temp = [...path];
// 往temp路径中压入当前节点的左子节点的节点值
temp.push(node.left.val);
// 并将temp路径保存进pathQueue中,经过这三步操作,就能保存此前的遍历结果了
pathQueue.push([...temp]);
}
// 下面也是和上面同理
if (node.right) {
nodeQueue.push(node.right);
let temp = [...path];
temp.push(node.right.val);
pathQueue.push([...temp]);
}
}
}
// 最后返回paths
return paths;
};
提交记录
执行结果:通过
208 / 208 个通过测试用例
执行用时:80 ms, 在所有 JavaScript 提交中击败了32.59%的用户
内存消耗:39.2 MB, 在所有 JavaScript 提交中击败了71.48%的用户
时间:2021/11/13 23:27
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
【更新结束】
更新:2021年11月13日22:26:33
【更新结束】
有关参考
更新:2021年11月13日22:27:47
参考:【算法-LeetCode】129. 求根节点到叶节点数字之和(递归;回溯)_赖念安的博客-优快云博客
更新:2021年11月13日23:31:37
参考:【算法-LeetCode】102. 二叉树的层序遍历(二叉树;层序遍历;BFS;生成二叉树)_赖念安的博客-优快云博客