Leetcode 几道 DFS 子树和路径问题杂记

563. 二叉树的坡度

给你一个二叉树的根节点 root ,计算并返回 整个树 的坡度 。

一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。

整个树 的坡度就是其所有节点的坡度之和。

思路

采用后序遍历的方式,在计算出左右子树的和之后,计算当前节点的坡度。

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def findTilt(self, root: Optional[TreeNode]) -> int:
        
        ans = 0
        def dfs(rt):
            if not rt:
                return 0
            l_val = dfs(rt.left)
            r_val = dfs(rt.right)

            nonlocal ans
            ans += abs(l_val - r_val)
            return rt.val + l_val + r_val

        dfs(root)
        return ans

508. 出现次数最多的子树元素和

给你一个二叉树的根结点 root ,请返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。

一个结点的 「子树元素和」 定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。

思路

参照 1339 的DFS遍历树的过程,对以当前节点为根的子树的和计数。最后根据计数输出出现次数最多的和的值。

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def findFrequentTreeSum(self, root: Optional[TreeNode]) -> List[int]:
        
        counter = defaultdict(int)
        def dfs(rt):
            nonlocal counter
            left_value = dfs(rt.left) if rt.left else 0
            right_value = dfs(rt.right) if rt.right else 0
            s = rt.val + left_value + right_value
            counter[s] += 1
            return s
        dfs(root)

        mx_value = int(-1e9)
        ans = []
        for value, count in counter.items():
            if count > mx_value:
                mx_value = count
                ans.clear()
                ans.append(value)
            elif count == mx_value:
                ans.append(value)
        return ans
                

1339. 分裂二叉树的最大乘积

给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。

思路

在树上删除 1 条边,会分裂成两个子树。整个树的节点和可以一次DFS求解出来。考虑每种子树划分的可能性,需要在DFS遍历树的过程中,保存以当前节点为根的子树的和。最后分别枚举每种删边的可能性,求解两部分乘积的最大值。

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxProduct(self, root: Optional[TreeNode]) -> int:
        s = 0
        mod = int(1e9+7)
        def calc_sum(rt):
            nonlocal s
            if not rt:
                return 0
            s = s + rt.val
            calc_sum(rt.left)
            calc_sum(rt.right)
    
        calc_sum(root)
        
        sums = []
        def dfs(rt):
            if not rt:
                return 0
            left_val = dfs(rt.left)
            right_val = dfs(rt.right)

            nonlocal sums
            nonlocal mod

            tree_sum = left_val + right_val + rt.val
            sums.append(tree_sum)
            return tree_sum
        dfs(root)

        sums = sums[:-1]
        ans = -1
        for part_a in sums:
            part_b =  s - part_a
            ans = max(ans, part_a * part_b)
        return ans % mod

124. 二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

思路

对于树上的任一节点,树上的最大路径和要么以该节点为根且经过该节点、要么不经过该节点。对任一节点,只需要考虑以该节点为根且经过该节点的最大路径是多少,通过对数的搜索求最大值即可。那么问题转换成,如何求解以该节点为根且经过该节点 的最大路径。

以该节点为根且经过该节点左子树的最大路径+右子树的最大路径+该节点的值,而左右子树的最大路径可以递归求解。需要注意的是,递归函数中的返回值是以该节点为根的最大路径:要么是左子树最大路径+该节点,要么是右子树最大路径+该节点。

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        ans = -inf

        def dfs(rt):
            if not rt:
                return 0
            
            left_value = dfs(rt.left)
            right_value = dfs(rt.right)

            nonlocal ans
            ans = max(ans, rt.val + left_value + right_value)
            return max(rt.val + max(left_value, right_value), 0)

        dfs(root)
        return ans

437. 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

思路

思路1 自底向上求解

顺延刚才子树的子路,采用后序遍历的方式,记录以当前节点为根下的所有路径和可能性。例如,对于示例1中左下角3 3 -1的子树,就有 3/-2/(3,3)/(-2,3) 四种路径的可能性。在搜索函数中,只需要先分别对左子树和右子树统计路径可能性,再统计经过当前节点的所有路径可能,最后统计满足目标和的路径数量。

考虑时间复杂度,对于每个节点,需要更新其下为根的所有路径,因此时间复杂度是n^2。

思路2 自顶向下求解

考虑从根节点到其下某节点的一个路径,如果以这个节点结尾的部分路径满足targetSum的要求,那么会有 n_i + ... + n_j = targetSum ,其中n_i是这个节点的值,n_j是路径中的某个节点的值,进一步转换成前缀和的形式,为s_i - targetSum = s_j ,其中s_i 是根节点到节点 i 的和,s_j 是根节点到节点 j的和。这一位置,对于路径中的任一节点,边统计根节点到当前节点的和,边计算之前的路径的前缀中,有多少个 s_i - targetSum 就可以完成计数。

这样的只需要遍历一遍所有节点,时间复杂度为 O(n).

代码

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
        # 思路2 前缀和
        ans = 0
        path_prefix = defaultdict(int)
        path_prefix[0] = 1
        def dfs(rt, s):
            nonlocal ans
            nonlocal path_prefix
            if not rt:
                return

            s += rt.val
            ans += path_prefix[s - targetSum]
            path_prefix[s] += 1
        
            dfs(rt.left, s)
            dfs(rt.right, s)
            path_prefix[s] -= 1
        
        dfs(root, 0)
        return ans

        # 思路 1 子树
        # ans = 0
        # def dfs(rt):
        #     if not rt:
        #         return {}
        #     left_path_cnt = dfs(rt.left)
        #     right_path_cnt = dfs(rt.right)
        #     node_path = defaultdict(int)
        #     node_path[rt.val] = 1

        #     for value, cnt in left_path_cnt.items():
        #         node_path[value + rt.val] += cnt
        #     for value, cnt in right_path_cnt.items():
        #         node_path[value + rt.val] += cnt

        #     nonlocal ans
        #     ans += node_path[targetSum]
        #     return node_path

        # dfs(root)
        # return ans
posted @ 2025-12-13 21:02  思言行知  阅读(3)  评论(0)    收藏  举报