祖父节点值为偶数的节点和

标签: 深度优先搜索 广度优先搜索 二叉树

难度: Medium

给你一棵二叉树,请你返回满足以下条件的所有节点的值之和:

  • 该节点的祖父节点的值为偶数。(一个节点的祖父节点是指该节点的父节点的父节点。)

如果不存在祖父节点值为偶数的节点,那么返回 0

示例:

输入:root = [6,7,8,2,7,1,3,9,null,1,4,null,null,null,5]
输出:18
解释:图中红色节点的祖父节点的值为偶数,蓝色节点为这些红色节点的祖父节点。

提示:

  • 树中节点的数目在 1 到 10^4 之间。
  • 每个节点的值在 1 到 100 之间。

Submission

运行时间: 56 ms

内存: 18.6 MB

# 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 sumEvenGrandparent(self, root: TreeNode) -> int:
        re = 0
        if not root.left and not root.right:
            return 0
        if root.val % 2 == 0:
            if root.left:
                if root.left.left:
                    re += root.left.left.val
                if root.left.right:
                    re += root.left.right.val
            if root.right:
                if root.right.left:
                    re += root.right.left.val
                if root.right.right:
                    re += root.right.right.val
        # 左子树也加入判断
        # 右子树也加入判断
        if root.left:
            re += self.sumEvenGrandparent(root.left)
        if root.right:
            re += self.sumEvenGrandparent(root.right)
        return re

Explain

这个解决方案使用深度优先搜索(DFS)来遍历整棵树,并在遍历过程中寻找符合条件的节点。首先检查当前节点值是否为偶数,如果是,那么它的孙子节点(如果存在)的值将被加到结果中。之后,递归地对左右子树应用同样的逻辑。这种方法确保了每个节点都被访问一次,且只有当节点的祖父节点的值为偶数时,节点的值才会被加入总和。

时间复杂度: O(n)

空间复杂度: O(n) in the worst case, O(log(n)) in the best case

# 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 sumEvenGrandparent(self, root: TreeNode) -> int:
        re = 0
        # Base case: if the node is a leaf, return 0
        if not root.left and not root.right:
            return 0
        # Check if the current node's value is even
        if root.val % 2 == 0:
            # Check left child's children
            if root.left:
                if root.left.left:
                    re += root.left.left.val
                if root.left.right:
                    re += root.left.right.val
            # Check right child's children
            if root.right:
                if root.right.left:
                    re += root.right.left.val
                if root.right.right:
                    re += root.right.right.val
        # Recursively sum up values for left subtree
        if root.left:
            re += self.sumEvenGrandparent(root.left)
        # Recursively sum up values for right subtree
        if root.right:
            re += self.sumEvenGrandparent(root.right)
        return re

Explore

即使当前节点为奇数,也有必要检查其子节点的子节点。这是因为当前节点的子节点可能是偶数,如果是偶数,根据题目要求,需要将其子节点(即当前节点的孙子节点)的值加入总和。因此,算法需要继续递归检查每个节点,以确保不遗漏任何符合条件的祖父节点。

在当前的解决方案中,对于空节点的处理是通过检查节点是否存在来实现的。例如,在尝试访问一个节点的左或右子节点之前,会先检查该节点是否存在。如果节点为空,则不会进行任何操作或递归调用,这样可以防止空引用异常。因此,基于当前的代码结构,对空节点的处理是安全的,不会导致程序错误。

在递归函数中,每一步重新计算子树的总和而不是传递累加值,这样做的优点是每个递归调用都是独立的,不依赖于外部状态,这使得代码更加简洁和易于理解。缺点是可能会增加计算的重复性,因为每个节点的值可能会在多个递归调用中被重复计算。不过,在此问题中,由于每个节点的贡献依据其祖父节点的状态而定,这种设计确保了计算的正确性。

函数中的基础情况处理是必要的,因为叶子节点没有子节点,因此也就没有孙子节点。如果叶子节点的父节点是偶数,即使这个叶子节点也是偶数,它也不会对最终结果有贡献,因为它没有孙子节点。这种处理确保了递归在正确的终止条件下停止,防止了不必要的递归调用,提高了效率。