Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1315. Sum of Nodes with Even-Valued Grandparent - Leetcode Solution

Code Implementation

# 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:
        def dfs(node, parent, grandparent):
            if not node:
                return 0
            total = 0
            if grandparent and grandparent.val % 2 == 0:
                total += node.val
            total += dfs(node.left, node, parent)
            total += dfs(node.right, node, parent)
            return total
        return dfs(root, None, None)
      
// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    int sumEvenGrandparent(TreeNode* root) {
        return dfs(root, nullptr, nullptr);
    }
    
    int dfs(TreeNode* node, TreeNode* parent, TreeNode* grandparent) {
        if (!node) return 0;
        int total = 0;
        if (grandparent && grandparent->val % 2 == 0) {
            total += node->val;
        }
        total += dfs(node->left, node, parent);
        total += dfs(node->right, node, parent);
        return total;
    }
};
      
// Definition for a binary tree node.
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

class Solution {
    public int sumEvenGrandparent(TreeNode root) {
        return dfs(root, null, null);
    }
    
    private int dfs(TreeNode node, TreeNode parent, TreeNode grandparent) {
        if (node == null) return 0;
        int total = 0;
        if (grandparent != null && grandparent.val % 2 == 0) {
            total += node.val;
        }
        total += dfs(node.left, node, parent);
        total += dfs(node.right, node, parent);
        return total;
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumEvenGrandparent = function(root) {
    function dfs(node, parent, grandparent) {
        if (!node) return 0;
        let total = 0;
        if (grandparent && grandparent.val % 2 === 0) {
            total += node.val;
        }
        total += dfs(node.left, node, parent);
        total += dfs(node.right, node, parent);
        return total;
    }
    return dfs(root, null, null);
};
      

Problem Description

You are given the root of a binary tree. Your task is to calculate the sum of all nodes whose grandparent node has an even value. A grandparent of a node is defined as the parent of its parent, if it exists.

  • Each node contains an integer value.
  • You must consider all nodes in the tree.
  • If a node does not have a grandparent, it is not included in the sum.
  • The binary tree structure is provided via the TreeNode definition.

The key challenge is to efficiently traverse the tree and, for each node, determine if its grandparent exists and has an even value. The sum of all such nodes' values should be returned.

Thought Process

At first glance, this problem seems to require checking every node's ancestry to determine if it has a grandparent with an even value. A brute-force approach might involve, for each node, traversing up to its parent and grandparent, checking the values, and then summing as needed.

However, this can be inefficient, especially for large trees, as it leads to repeated work. Instead, it's more efficient to traverse the tree in a way that, at each node, you already know the values of its parent and grandparent. This can be accomplished by passing information down the recursion as you traverse the tree.

The conceptual shift is from "for each node, look up" to "as you go down, carry information about ancestors." This avoids repeated traversals and makes the solution much cleaner and faster.

Solution Approach

  • Recursive Traversal (DFS): We use a depth-first search (DFS) to traverse the tree. At each recursive call, we keep track of the current node, its parent, and its grandparent.
  • Passing Ancestor Information: When recursing, we pass the current node as the new parent and the current parent as the new grandparent for the child calls.
  • Checking Grandparent's Value: At each node, if the grandparent exists and its value is even, we add the current node's value to our sum.
  • Aggregating the Result: We sum the values from the left and right subtrees recursively, along with the value from the current node if it qualifies.
  • Base Case: If the current node is null, we return zero.

This approach ensures that each node is visited only once, and the necessary ancestor information is always available without extra lookups. It's efficient and easy to implement in a recursive manner.

Example Walkthrough

Let's consider the following binary tree:

        6
       / \
      7   8
     / \ / \
    2  7 1  3
   /  / \
  9  1   4
  

We need to sum all nodes whose grandparent has an even value.

  • Node 2's grandparent is 6 (even) → include 2
  • Node 7 (leftmost) grandparent is 6 (even) → include 7
  • Node 1 (right child of 7) grandparent is 7 (odd) → skip
  • Node 4 grandparent is 7 (odd) → skip
  • Node 9 grandparent is 7 (odd) → skip
  • Node 1 (left child of 8) grandparent is 6 (even) → include 1
  • Node 3 grandparent is 6 (even) → include 3

The sum is 2 + 7 + 1 + 3 = 13.

The algorithm traverses the tree recursively, always knowing the parent and grandparent at each step, and accumulates the sum as described.

Time and Space Complexity

  • Brute-Force Approach: For each node, traversing up to find its grandparent can take O(h) time (where h is the tree's height), leading to O(nh) total time for n nodes.
  • Optimized Approach (Current Solution): Each node is visited exactly once, and all operations per node are O(1). Thus, the total time complexity is O(n).
  • Space Complexity: The space used is O(h) due to the recursion stack, where h is the height of the tree. In the worst case (skewed tree), this is O(n), and in the best case (balanced tree), it's O(log n).

Summary

This problem is elegantly solved by a recursive traversal that carries parent and grandparent information at each step. By doing this, we avoid redundant ancestor lookups and ensure each node is processed efficiently. The approach leverages the natural structure of recursion in trees and demonstrates the power of passing context through recursive calls. The result is a clean, O(n) solution that is easy to reason about and implement.