一、原题链接
[437.路径总和Ⅲ](https://leetcode-cn.com/problems/path-sum-iii/)
二、解题思路
总体而言,解这道题目是利用前缀和的思想。所谓前缀和,就是指在一条路径上的有某个元素A,A之前所有元素的和(包括A),称之为前缀和。
路径上存在元素A和B(A和B有前后关系),如果A和B的前缀和相等,那么我们可以推测出A、B之间的元素和为0;如果元素B的前缀和比A的前缀和多sum,则说明A、B之间的元素和为sum。
利用栈来实现前缀遍历,栈中元素为元组(node,prefixsum),其中prefixsum是一个列表,记录从根结点到达当前结点node的前缀和。
为了简单说明,以结点的值代表结点。 初始化栈为(10,[10]);弹出栈顶元素,访问其左子结点,并将(5,[15, 5])入栈,访问其右子结点,将(-3,[7,-3])入栈……
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> int:
if not root:
return 0
stack = [(root, [root.val])]
cnt = 0 # 记录路径和
while stack:
node, prefix_sum = stack.pop()
cnt += prefix_sum.count(sum)
prefix_sum.append(0)
if node.left:
# 更新前缀和
tmp = [val+node.left.val for val in prefix_sum]
# 左子结点入栈
stack.append((node.left, tmp))
if node.right:
tmp = [val+node.right.val for val in prefix_sum]
stack.append((node.right, tmp))
return cnt
2、递归处理
同样是基于前缀和以及前缀和哈希表,不过利用递归来实现。 有一点需要注意,一定要将前缀和哈希表初始化为{0:1}而不是初始化为空。考虑下图所示情况,sum为22:
如果初始化哈希表为空,递归从根结点开始:
根结点5:哈希表更新为{5:1};
左子结点4:哈希表更新为{5:1, 9:1};
左子结点11:哈希表更新为{5:1,9:1,20:1};
左子结点7:此时的前缀和为20+7=27,27-22=5,哈希表中存在前缀和为5的项,因此找到一条和为22的路径,将count加1,同时哈希表更新为{5:1, 9:1, 20:1, 27:1};
此时,结点7为叶子结点,树的最左分支处理完毕,要回溯处理结点11的右子结点,因此,在退回之前,应该将哈希表中key值为27的项的value-1,哈希表更新为{5:1, 9:1, 20:1, 27:0};
右子结点2:此时前缀和为20+2=22,但是在哈希表初始化为空的情况下不存在key为0的项,因此也就不记录这一条路径!因此需要将哈希表初始化为{0:1}。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> int:
self.count = 0 # 记录符合条件的路径数目
prefixSumArray = {0:1} # 初始化前缀和哈希表为{0:1}
prefixSum = 0 # 记录当前结点的前缀和
self.dfs(root, sum, prefixSumArray, prefixSum) # 深搜处理
return self.count
def dfs(self, root, sum, prefixSumArray, prefixSum):
# 结点如果是None,返回
if not root:
return 0
# 更新前缀和,加上当前结点的val
prefixSum += root.val
# 查看哈希表中是否存在和为sum的路径,存在的话更新count
self.count += prefixSumArray.get(prefixSum-sum, 0)
# 将当前的前缀和记录到哈希表中
prefixSumArray[prefixSum] = prefixSumArray.get(prefixSum, 0) + 1
# 遍历处理左子树和右子树
self.dfs(root.left, sum, prefixSumArray, prefixSum)
self.dfs(root.right, sum, prefixSumArray, prefixSum)
# 处理完子树回退之前,记得要将对应前缀和的value值减一,如上述绿色文字所示。
prefixSumArray[prefixSum] -= 1