二叉树的路径问题

Posted 宇哥在学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树的路径问题相关的知识,希望对你有一定的参考价值。

今天刷了几道关于二叉树的路径问题,总结了一些套路。

 // 树的数据结构基于以下 // 代码使用 javascript 实现 function TreeNode(val) {  this.val = val;   this.left = this.right = null; }


257. 二叉树的所有路径


题目链接https://leetcode-cn.com/problems/binary-tree-paths/

题意:给定一个二叉树,返回所有从根节点到叶子节点的路径。说明: 叶子节点是指没有子节点的节点。

    1   /  \  2   3   \       5输出: ["1->2->5", "1->3"]解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3

利用辅助函数实现路径拼接的逻辑,再递归调用左右节点,代码如下:

var binaryTreePaths = function(root) { const res = []; if (root === null) return res; help(root, res, ''); return res;};
var help = function(root, res, cur) { if (root === null) return; cur += root.val if (root.left === null && root.right === null) { res.push(cur) } else { help(root.left, res, `${cur}->`); help(root.right, res, `${cur}->`); }}


404. 左叶子之和


题目链接https://leetcode-cn.com/problems/sum-of-left-leaves/

题意:计算给定二叉树的所有左叶子之和。

    3   / \  9  20    /  \   15   7输出:24解释:在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

解题思路同样是利用递归,但这次不需要借助辅助函数,只有满足左节点不为空且当前左节点的子节点全部为空时,开始累加。并从根节点开始左右递归累加。

var sumOfLeftLeaves = function(root) { if (root === null) return 0; let num = 0; if (root.left !== null && (root.left.left === null && root.left.right === null)) { num += root.left.val; } return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right) + num; };


113. 路径总和 ||


题目链接:https://leetcode-cn.com/problems/path-sum-ii/

题意:给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。说明: 叶子节点是指没有子节点的节点。

 5 / \ 4 8 / / \ 11 13 4 / \ / \        7    2  5   1给定如下二叉树,以及目标和 sum = 22输出:[  [5,4,11,2],  [5,8,4,5]]        

这道题的思路跟 257 很相似,也是需要借助辅助函数和递归来处理,需要注意的是 cur 这个变量往下传递时,应该传递它的副本,否则会造成引用问题。

var pathSum = function(root, sum) { const res = [];  if (root === nullreturn res; help(root, res, [], sum); return res;};
var help = function(root, res, cur, target) {  if (root === nullreturn; cur.push(root.val); target -= root.val; if (root.left === null && root.right === null && target === 0) { res.push(cur); return; } else { help(root.left, res, cur.slice(), target); help(root.right, res, cur.slice(), target); } }


437. 路径总和 |||


题目链接https://leetcode-cn.com/problems/path-sum-iii/

题意:给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

说明:二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10 / \ 5 -3 / \ \ 3 2 11 / \ \3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 32. 5 -> 2 -> 13.  -3 -> 11

第一种结题的思路与前面几道相似,但不同点在于这次的路径不用从根节点开始,只要满足条件可在任意接节点结束,所以需要用到两层递归,主函数的初次调用除了递归调用左右子节点,还需要借助辅助函数完成另一层递归。代码如下:

var pathSum = function(root, sum) { if (root === null) return 0; return pathSum(root.left, sum) + pathSum(root.right, sum) + help(root, sum);};
var help = function(root, sum) { if (root === null) return 0; let res = 0; if (root.val == sum) { res++; } return help(root.left, sum - root.val) + help(root.right, sum - root.val) + res;}

但是此方式的时间复杂度O(nlogn)



有一定的优化空间,考虑采用前缀和法,利用 JS 的 Map 这一数据结构,用路径和作 key,路径和出现的次数作 value。假设某一处的路径和为 x,往下搜索树至另一处它的路径和为 y。只要满足 y - x = sum 则总数增加,需要注意的是完成一次递归调用,需要将 map 中对应的路径和次数减 1,完整的代码如下:


var pathSum = function(root, sum) { const map = new Map(); map.set(0, 1); return help(root, 0, sum, map);}
var help = function(root, sum, target, map) { if (root === null) return 0; sum += root.val; let res = map.get(sum - target) || 0; map.set(sum, (map.get(sum) || 0) + 1); res += help(root.left, sum, target, map) + help(root.right, sum, target, map); map.set(sum, map.get(sum) - 1); return res;}

这样的写法,时间复杂度为 O(n),可以看到速度提升了不少。

以上是关于二叉树的路径问题的主要内容,如果未能解决你的问题,请参考以下文章

C语言,如何用非递归方法输出二叉树的根到所有叶子路径?

新手算法学习之路----二叉树(二叉树的路径和)

二叉树的外路径长、内路径长及相互关系公式证明

代码随想录算法训练营第17天 | ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

二叉树13:二叉树的所有路径

LeetCode与《代码随想录》二叉树篇:做题笔记与总结-JavaScript版