树的求和属于树的题目中比较常见的,因为可以有几种变体,灵活度比较高,也可以考察到对于树的数据结构和递归的理解。一般来说这些题目就不用考虑非递归的解法了(虽然其实道理是跟LeetCode总结 -- 树的遍历篇一样的,只要掌握了应该没问题哈)。 LeetCode中关于树的求和有以下题目:
Path Sum
Path Sum II
Sum Root to Leaf Numbers
Binary Tree Maximum Path Sum
我们先来看看最常见的题目Path Sum。这道题是判断是否存在从根到叶子的路径和跟给定sum相同。树的题目基本都是用递归来解决,主要考虑两个问题:
1)如何把问题分治成子问题给左子树和右子树。这里就是看看左子树和右子树有没有存在和是sum减去当前结点值得路径,只要有一个存在,那么当前结点就存在路径。
2)考虑结束条件是什么。这里的结束条件一个是如果当前节点是空的,则返回false。另一个如果是叶子,那么如果剩余的sum等于当前叶子的值,则找到满足条件的路径,返回true。
想清楚上面两个问题,那么实现起来就是一次树的遍历,按照刚才的分析用参数或者返回值传递需要维护的值,然后按照递归条件和结束条件进行返回即可。算法的时间复杂度是一次遍历O(n),空间复杂度是栈的大小O(logn)。
对于Path Sum II,其实思路和Path Sum是完全一样的,只是需要输出所有路径,所以需要数据结构来维护路径,添加两个参数,一个用来维护走到当前结点的路径,一个用来保存满足条件的所有路径,思路上递归条件和结束条件是完全一致的,空间上这里会依赖于结果的数量了。
Sum Root to Leaf Numbers这道题多了两个变化,一个是每一个结点相当于位上的值,而不是本身有权重,不过其实没有太大变化,每一层乘以10加上自己的值就可以了。另一个变化就是要把所有路径累加起来,这个其实就是递归条件要进行调整,Path Sum中是判断左右子树有一个找到满足要求的路径即可,而这里则是把左右子树的结果相加返回作为当前节点的累加结果即可。
变化比较大而且有点难度的是Binary Tree Maximum Path Sum,这道题目的路径要求不再是从根到叶子的路径,这个题目是把树完全看成一个无向图,然后寻找其中的路径。想起来就觉得比上面那种麻烦许多,不过仔细考虑会发现还是有章可循的,找到一个根节点最大路径,无非就是找到左子树最大路径,加上自己的值,再加上右子树的最大路径(这里左右子树的路径有可能不取,如果小于0的话)。我们要做的事情就是对于每个结点都做一次上面说的这个累加。而左子树最大路径和右子树最大路径跟Path Sum II思路是比较类似的,虽然不一定要到叶子节点,不过标准也很简单,有大于0的就取,如果走下去路径和小于0那么就不取。从分治的角度来看,左右子树的最大路径就是取自己的值加上Max(0,左子树最大路径,右子树最大路径)。这么一想也就不用考虑那么多细节了。而通过当前节点的最长路径则是自己的值+Max(0,左子树最大路径)+Max(0,右子树最大路径)。所以整个算法就是维护这两个量,一个是自己加上左或者右子树最大路径作为它的父节点考虑的中间量,另一个就是自己加上左再加上右作为自己最大路径。具体的实现可以参见Binary Tree Maximum Path Sum。
这篇总结主要讲了LeetCode中关于树的求和的题目。总体来说,求和路径有以下三种:(1)根到叶子结点的路径;(2)父结点沿着子结点往下的路径;(3)任意结点到任意结点(也就是看成无向图)。这几种路径方式在面试中经常灵活变化,不同的路径方式处理题目的方法也会略有不同,不过最复杂也就是Binary Tree Maximum Path Sum这种路径方式,只要考虑清楚仍然是一次递归遍历的问题哈。