1315. Sum of Nodes with Even-Valued Grandparent
Problem Description
You are given the root
of a binary tree. Your task is to find the sum of values of all nodes that have an even-valued grandparent.
A grandparent of a node is defined as the parent of its parent (if it exists). In other words, if a node has a parent, and that parent also has a parent, then that parent's parent is the node's grandparent.
The problem asks you to:
- Traverse the binary tree
- For each node, check if its grandparent exists and has an even value
- If a node has an even-valued grandparent, include that node's value in the sum
- Return the total sum of all such nodes
If there are no nodes with an even-valued grandparent in the tree, return 0
.
For example, if a node with value 6
(which is even) has two children, and those children have their own children (the grandchildren of the node with value 6
), then all those grandchildren should have their values included in the sum.
The solution uses a depth-first search (DFS) approach where it tracks the parent's value as it traverses the tree. When visiting a node, if the parent's parent (tracked from the previous recursive call) has an even value, the current node's children are the ones that should be added to the sum.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem involves a binary tree, which is a special type of graph with nodes (tree nodes) and edges (parent-child relationships).
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree structure where we have a root node and each node has at most two children (left and right).
DFS
- Since we're dealing with a tree structure, the flowchart directs us to use DFS (Depth-First Search).
Why DFS is appropriate for this problem:
- We need to traverse the entire tree to find all nodes with even-valued grandparents
- We need to track information about ancestors (specifically grandparents) as we traverse
- DFS naturally maintains the parent-child-grandchild relationship through its recursive call stack
- As we traverse down the tree, we can pass information about the parent and grandparent values to determine if current nodes should be included in the sum
Conclusion: The flowchart correctly suggests using DFS for this tree traversal problem. The DFS approach allows us to maintain the grandparent-parent-child relationship naturally through recursive calls, where we can pass the parent's value down to check if it's even when we reach the grandchildren nodes.
Intuition
The key insight is that when we're at a node, we can't directly check if its grandparent is even-valued because we typically only have access to the current node in a tree traversal. However, we can flip our perspective: instead of checking "does this node have an even-valued grandparent?", we can ask "if this node has an even value, which nodes should I add to the sum?"
When we're at a node with an even value, its grandchildren are the nodes that should be counted. But how do we access grandchildren? We can't directly jump two levels down. Instead, we can pass information down through the traversal.
The clever approach is to pass the parent's value as we traverse. Here's the reasoning:
- When we start at the root, we pass down the root's value to its children
- When we're at a child node, we receive its parent's value as a parameter
- If this parent value is even, then the current node's children are grandchildren of that even-valued node
- So we add the values of the current node's children to our sum
This way, we're essentially checking one generation ahead: "My parent has an even value, so my children are grandchildren of an even-valued node."
The initial call uses dfs(root, 1)
with 1
as the parent value (which is odd) to ensure that the root's children aren't mistakenly counted, since the root itself has no grandparent.
This approach elegantly solves the problem in a single traversal by maintaining just enough context (the parent's value) to make the grandparent-grandchild relationship determination possible.
Learn more about Tree, Depth-First Search, Breadth-First Search and Binary Tree patterns.
Solution Approach
The solution implements a DFS traversal with a clever parameter passing technique. Let's walk through the implementation step by step:
Function Design:
We create a helper function dfs(root, x)
where:
root
is the current node being processedx
is the value of the parent node ofroot
Base Case:
If root
is None
, we return 0
since there's nothing to process.
Recursive Traversal: For each node, we recursively calculate the sum for its left and right subtrees:
ans = dfs(root.left, root.val) + dfs(root.right, root.val)
Notice how we pass root.val
as the second parameter - this means when we process the children, they'll know their parent's value.
Checking for Even Grandparent: The critical logic happens here:
if x % 2 == 0: if root.left: ans += root.left.val if root.right: ans += root.right.val
If x
(the parent's value) is even, then the current node's children are grandchildren of that even-valued node. We add their values to our answer.
Initial Call:
The solution starts with dfs(root, 1)
. We use 1
as the initial parent value because:
1
is odd, ensuring the root's children won't be mistakenly counted- The root has no actual parent or grandparent
Time and Space Complexity:
- Time:
O(n)
wheren
is the number of nodes, as we visit each node exactly once - Space:
O(h)
whereh
is the height of the tree, due to the recursive call stack
The elegance of this solution lies in how it tracks the grandparent relationship indirectly - by passing the parent value down and checking it when we're one level deeper, we effectively identify all nodes with even-valued grandparents.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this binary tree:
6 (even) / \ 7 8 / \ \ 2 4 5
We want to find the sum of all nodes with an even-valued grandparent.
Step 1: Start at root
- Call
dfs(node_6, 1)
- Current node: 6, parent value: 1 (odd, dummy value)
- Since 1 is odd, we don't add node 6's children (7 and 8) to the sum
- Recursively process left child (7) and right child (8)
Step 2: Process node 7
- Call
dfs(node_7, 6)
- Current node: 7, parent value: 6 (even!)
- Since 6 is even, we add node 7's children to the sum:
- Left child: 2 (add to sum)
- Right child: 4 (add to sum)
- Current sum = 2 + 4 = 6
- Recursively process children with parent value 7
Step 3: Process node 8
- Call
dfs(node_8, 6)
- Current node: 8, parent value: 6 (even!)
- Since 6 is even, we add node 8's children to the sum:
- Left child: None (skip)
- Right child: 5 (add to sum)
- Current sum = 6 + 5 = 11
- Recursively process children with parent value 8
Step 4: Process leaf nodes (2, 4, 5)
dfs(node_2, 7)
: parent 7 is odd, no children to add anywaydfs(node_4, 7)
: parent 7 is odd, no children to add anywaydfs(node_5, 8)
: parent 8 is even, but node 5 has no children
Final Result: 2 + 4 + 5 = 11
The nodes 2, 4, and 5 all have node 6 as their grandparent (which has an even value), so their values are included in the sum.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8class Solution:
9 def sumEvenGrandparent(self, root: TreeNode) -> int:
10 """
11 Calculate the sum of all nodes that have an even-valued grandparent.
12
13 Args:
14 root: The root node of the binary tree
15
16 Returns:
17 The sum of all nodes with even-valued grandparents
18 """
19
20 def dfs(node: TreeNode, parent_val: int) -> int:
21 """
22 Depth-first search to find sum of nodes with even grandparents.
23
24 Args:
25 node: Current node being processed
26 parent_val: Value of the parent node (potential grandparent for children)
27
28 Returns:
29 Sum of nodes with even grandparents in this subtree
30 """
31 # Base case: if node is None, return 0
32 if node is None:
33 return 0
34
35 # Recursively calculate sum for left and right subtrees
36 # Pass current node's value as the parent value for children
37 total_sum = dfs(node.left, node.val) + dfs(node.right, node.val)
38
39 # If parent value is even, current node's children have an even grandparent
40 if parent_val % 2 == 0:
41 # Add left child's value if it exists
42 if node.left:
43 total_sum += node.left.val
44 # Add right child's value if it exists
45 if node.right:
46 total_sum += node.right.val
47
48 return total_sum
49
50 # Start DFS with root node and odd parent value (1) to avoid counting root's children
51 return dfs(root, 1)
52
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 /**
18 * Calculates the sum of all nodes with even-valued grandparents in a binary tree.
19 *
20 * @param root The root node of the binary tree
21 * @return The sum of values of nodes with even-valued grandparents
22 */
23 public int sumEvenGrandparent(TreeNode root) {
24 // Start DFS with initial parent value of 1 (odd) to avoid false positives
25 return dfs(root, 1);
26 }
27
28 /**
29 * Performs depth-first search to find and sum nodes with even-valued grandparents.
30 *
31 * @param currentNode The current node being processed
32 * @param grandparentValue The value of the grandparent node (parent of parent)
33 * @return The sum of node values whose grandparents have even values
34 */
35 private int dfs(TreeNode currentNode, int grandparentValue) {
36 // Base case: if current node is null, return 0
37 if (currentNode == null) {
38 return 0;
39 }
40
41 // Recursively calculate sum for left and right subtrees
42 // Pass current node's value as the grandparent value for its grandchildren
43 int sum = dfs(currentNode.left, currentNode.val) + dfs(currentNode.right, currentNode.val);
44
45 // If the grandparent value is even, add the values of current node's children
46 // (These children have an even-valued grandparent)
47 if (grandparentValue % 2 == 0) {
48 if (currentNode.left != null) {
49 sum += currentNode.left.val;
50 }
51 if (currentNode.right != null) {
52 sum += currentNode.right.val;
53 }
54 }
55
56 return sum;
57 }
58}
59
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 int sumEvenGrandparent(TreeNode* root) {
15 // Lambda function for DFS traversal
16 // Parameters: current node and its parent's value
17 function<int(TreeNode*, int)> dfs = [&](TreeNode* node, int parentVal) {
18 // Base case: if node is null, return 0
19 if (!node) {
20 return 0;
21 }
22
23 // Recursively calculate sum from left and right subtrees
24 // Pass current node's value as the parent value for children
25 int sum = dfs(node->left, node->val) + dfs(node->right, node->val);
26
27 // If parent value is even, current node's children are grandchildren
28 // of an even-valued grandparent
29 if (parentVal % 2 == 0) {
30 // Add left child's value if it exists
31 if (node->left) {
32 sum += node->left->val;
33 }
34 // Add right child's value if it exists
35 if (node->right) {
36 sum += node->right->val;
37 }
38 }
39
40 return sum;
41 };
42
43 // Start DFS from root with parent value 1 (odd number)
44 // This ensures root's children won't be counted as grandchildren
45 return dfs(root, 1);
46 }
47};
48
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Calculates the sum of all nodes with even-valued grandparents in a binary tree
17 * @param root - The root node of the binary tree
18 * @returns The sum of node values that have even-valued grandparents
19 */
20function sumEvenGrandparent(root: TreeNode | null): number {
21 /**
22 * Depth-first search helper function to traverse the tree
23 * @param currentNode - The current node being processed
24 * @param parentValue - The value of the parent node (used to determine if it's an even grandparent)
25 * @returns The sum of nodes with even grandparents in the current subtree
26 */
27 const dfs = (currentNode: TreeNode | null, parentValue: number): number => {
28 // Base case: if current node is null, return 0
29 if (!currentNode) {
30 return 0;
31 }
32
33 // Destructure current node properties
34 const { val: currentValue, left: leftChild, right: rightChild } = currentNode;
35
36 // Recursively calculate sum for left and right subtrees
37 // Pass current node's value as the parent value for children
38 let totalSum: number = dfs(leftChild, currentValue) + dfs(rightChild, currentValue);
39
40 // If the parent's value (which is grandparent to current node's children) is even,
41 // add the values of current node's children to the sum
42 if (parentValue % 2 === 0) {
43 totalSum += leftChild?.val ?? 0; // Add left child's value if it exists
44 totalSum += rightChild?.val ?? 0; // Add right child's value if it exists
45 }
46
47 return totalSum;
48 };
49
50 // Start DFS from root with initial parent value of 1 (odd number)
51 // This ensures root's parent won't be considered as even
52 return dfs(root, 1);
53}
54
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs a depth-first search (DFS) traversal of the binary tree. Each node is visited exactly once during the traversal. At each node, the algorithm performs constant time operations:
- Checking if the node is null
- Checking if the parent value
x
is even using modulo operation - Adding values of children nodes if they exist
- Making recursive calls to left and right children
Since we visit each of the n
nodes exactly once and perform O(1)
operations at each node, the overall time complexity is O(n)
.
Space Complexity: O(n)
The space complexity is determined by the recursive call stack used during the DFS traversal. In the worst case, the tree could be completely unbalanced (like a linked list), where all nodes are arranged in a single path. This would result in a recursion depth of n
, requiring O(n)
space on the call stack.
In the best case of a perfectly balanced tree, the recursion depth would be O(log n)
, but since we consider worst-case complexity, the space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Tracking Grandparent Relationships
A common mistake is trying to track the grandparent node directly or passing both parent and grandparent values through the recursion. This leads to unnecessarily complex code:
Incorrect Approach:
def dfs(node, parent, grandparent):
if not node:
return 0
total = dfs(node.left, node, parent) + dfs(node.right, node, parent)
if grandparent and grandparent.val % 2 == 0:
total += node.val
return total
This approach requires passing node references and handling None cases, making the code more error-prone.
2. Wrong Level Checking
Another pitfall is checking at the wrong level of the tree. Some might incorrectly add the current node's value when its parent is even:
Incorrect Code:
if parent_val % 2 == 0: total_sum += node.val # Wrong! This makes parent the grandparent
Correct Code:
if parent_val % 2 == 0: if node.left: total_sum += node.left.val # Correct! Parent becomes grandparent to children if node.right: total_sum += node.right.val
3. Forgetting to Check Child Existence
A subtle but critical error is forgetting to check if children exist before accessing their values:
Incorrect Code:
if parent_val % 2 == 0: total_sum += node.left.val + node.right.val # Will crash if either child is None
Correct Code:
if parent_val % 2 == 0: if node.left: total_sum += node.left.val if node.right: total_sum += node.right.val
4. Wrong Initial Parent Value
Using an even number (like 0 or 2) as the initial parent value would incorrectly count the root's children as having an even grandparent:
Incorrect:
return dfs(root, 0) # Wrong! Will incorrectly add root's grandchildren
Correct:
return dfs(root, 1) # Correct! Odd number prevents false counting
5. Alternative Solution Using Level Tracking
For those who find the parent-value passing confusing, here's an alternative approach that explicitly tracks the grandparent:
def sumEvenGrandparent(self, root: TreeNode) -> int:
def dfs(node, parent_val, grandparent_val):
if not node:
return 0
total = 0
# Check if current node has an even grandparent
if grandparent_val != -1 and grandparent_val % 2 == 0:
total += node.val
# Recurse with updated parent and grandparent values
total += dfs(node.left, node.val, parent_val)
total += dfs(node.right, node.val, parent_val)
return total
return dfs(root, -1, -1) # Use -1 to indicate no parent/grandparent
While this approach is more explicit, it's also slightly less efficient due to passing an extra parameter.
How does quick sort divide the problem into subproblems?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!