Facebook Pixel

1022. Sum of Root To Leaf Binary Numbers

Problem Description

You have a binary tree where each node contains either a value of 0 or 1.

Each path from the root node to a leaf node represents a binary number. The binary number is formed by reading the node values along the path from top to bottom, where the root node's value is the most significant bit.

For example, if you traverse a path with values 0 -> 1 -> 1 -> 0 -> 1, this represents the binary number 01101, which equals 13 in decimal.

Your task is to:

  1. Find all root-to-leaf paths in the tree
  2. Convert each path to its decimal equivalent
  3. Return the sum of all these decimal values

The problem guarantees that the final answer will fit within a 32-bit integer.

Example walkthrough:

  • If a path is 1 -> 0 -> 1, the binary number is 101, which equals 5 in decimal
  • If another path is 1 -> 1, the binary number is 11, which equals 3 in decimal
  • The sum would be 5 + 3 = 8

The solution uses a depth-first search (DFS) approach with bit manipulation. As we traverse down the tree, we build the binary number by shifting the current value left by one position (t << 1) and adding the current node's value using bitwise OR (| root.val). When we reach a leaf node (a node with no children), we add the accumulated binary number to our 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 need to traverse from root to leaf nodes.

DFS

  • We arrive at DFS: Since we're dealing with a tree and need to explore complete paths from root to leaf nodes, DFS is the natural choice.

Why DFS is appropriate for this problem:

  • We need to traverse complete paths from root to leaf (depth-oriented traversal)
  • We need to maintain the current binary number as we go down each path
  • DFS allows us to naturally track the path state through recursion or a stack
  • When we reach a leaf, we can immediately process the complete binary number
  • The recursive nature of DFS makes it easy to accumulate values as we traverse down and automatically backtrack to explore other paths

Conclusion: The flowchart correctly leads us to use DFS for this tree traversal problem. The DFS approach allows us to efficiently explore all root-to-leaf paths while building up the binary numbers incrementally using bit manipulation (t = (t << 1) | root.val), and sum them up when we reach the leaf nodes.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we look at this problem, we need to think about how binary numbers are constructed. In a binary number like 101, each digit represents a power of 2, reading from left to right with decreasing powers. The leftmost digit is the most significant bit.

As we traverse down a path in the tree, we're essentially building a binary number digit by digit. Each time we move to a child node, we're adding a new digit to the right of our current binary number. This is exactly what happens when we shift a binary number left by one position and add a new bit.

For example, if we have the binary number 10 (decimal 2) and we encounter a node with value 1, we want to form 101 (decimal 5). We can achieve this by:

  1. Shifting 10 left by one position to get 100 (decimal 4)
  2. Adding the new bit 1 to get 101 (decimal 5)

This operation can be written as t = (t << 1) | root.val, where << is the left shift operator and | is the bitwise OR operation.

The key insight is that we need to explore every possible root-to-leaf path and calculate its decimal value. DFS is perfect for this because:

  • It naturally explores complete paths before backtracking
  • We can pass the accumulated binary value as a parameter during recursion
  • When we reach a leaf node, we have the complete binary number for that path
  • The recursive calls automatically handle the summation of all paths

The beauty of this approach is that we don't need to store the entire path or convert strings to numbers. We build the decimal value incrementally as we traverse, making the solution both elegant and efficient.

Learn more about Tree, Depth-First Search and Binary Tree patterns.

Solution Approach

The solution implements a recursive DFS approach with bit manipulation to efficiently calculate the sum of all root-to-leaf binary numbers.

Core Algorithm Structure:

We define a helper function dfs(root, t) that takes two parameters:

  • root: the current node being processed
  • t: the accumulated binary number (in decimal) from the root to the current node

Step-by-step Implementation:

  1. Base Case - Null Node: If the current node is None, we return 0. This handles the case where we might call DFS on a non-existent child.

  2. Build the Binary Number: For each node, we update the accumulated value using bit manipulation:

    t = (t << 1) | root.val
    • t << 1 shifts the current binary number left by one position (equivalent to multiplying by 2)
    • | root.val adds the current node's value as the least significant bit

    For example, if t = 5 (binary 101) and root.val = 1, then:

    • t << 1 gives us 10 (binary 1010)
    • 10 | 1 gives us 11 (binary 1011)
  3. Leaf Node Detection: When we reach a leaf node (both root.left and root.right are None), we've completed a root-to-leaf path. We return the accumulated value t, which represents the decimal value of the binary number formed by this path.

  4. Recursive Exploration: For non-leaf nodes, we recursively explore both subtrees:

    return dfs(root.left, t) + dfs(root.right, t)

    This automatically sums up the values from all paths in the left and right subtrees.

  5. Initial Call: We start the recursion with dfs(root, 0), beginning with an accumulated value of 0.

Why This Works:

The recursive nature of DFS ensures that:

  • Each path is explored completely before backtracking
  • The binary number is built incrementally as we go deeper
  • The sum is accumulated naturally through the recursive returns
  • No additional data structures are needed to store paths or intermediate results

The time complexity is O(n) where n is the number of nodes, as we visit each node exactly once. The space complexity is O(h) where h is the height of the tree, due to the recursion stack.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate how the solution works.

Consider this binary tree:

       1
      / \
     0   1
    / \
   0   1

This tree has three root-to-leaf paths:

  • Path 1: 1 -> 0 -> 0
  • Path 2: 1 -> 0 -> 1
  • Path 3: 1 -> 1

Step-by-step DFS traversal:

  1. Start at root (value=1):

    • t = 0 (initial value)
    • Update: t = (0 << 1) | 1 = 0 | 1 = 1
    • Not a leaf, so recurse on both children
  2. Go left to node (value=0):

    • Current t = 1 (binary: 1)
    • Update: t = (1 << 1) | 0 = 2 | 0 = 2 (binary: 10)
    • Not a leaf, continue recursing
  3. Go left again to node (value=0):

    • Current t = 2 (binary: 10)
    • Update: t = (2 << 1) | 0 = 4 | 0 = 4 (binary: 100)
    • This is a leaf! Return 4
  4. Backtrack and go right to node (value=1):

    • Current t = 2 (binary: 10)
    • Update: t = (2 << 1) | 1 = 4 | 1 = 5 (binary: 101)
    • This is a leaf! Return 5
  5. Backtrack to root and go right to node (value=1):

    • Current t = 1 (binary: 1)
    • Update: t = (1 << 1) | 1 = 2 | 1 = 3 (binary: 11)
    • This is a leaf! Return 3

Final calculation:

  • Path 1 -> 0 -> 0 = binary 100 = decimal 4
  • Path 1 -> 0 -> 1 = binary 101 = decimal 5
  • Path 1 -> 1 = binary 11 = decimal 3
  • Total sum = 4 + 5 + 3 = 12

The bit manipulation elegantly builds each binary number:

  • Starting with 0, we shift and add bits as we traverse
  • (0 << 1) | 1 = 1 gives us the first bit
  • (1 << 1) | 0 = 2 adds a zero to get 10
  • (2 << 1) | 1 = 5 adds a one to get 101

Each recursive call returns the sum of all paths in its subtree, naturally accumulating the total.

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 sumRootToLeaf(self, root: TreeNode) -> int:
10        """
11        Calculate the sum of all root-to-leaf paths where each path represents a binary number.
12      
13        Args:
14            root: The root node of the binary tree
15          
16        Returns:
17            The sum of all root-to-leaf binary numbers
18        """
19      
20        def dfs(node: TreeNode, current_value: int) -> int:
21            """
22            Depth-first search to traverse the tree and calculate path sums.
23          
24            Args:
25                node: Current node in the traversal
26                current_value: The accumulated binary value from root to current node
27              
28            Returns:
29                Sum of all binary numbers from this subtree
30            """
31            # Base case: if node is None, contribute 0 to the sum
32            if node is None:
33                return 0
34          
35            # Shift current value left by 1 bit and add current node's value
36            # This effectively appends the current bit to the binary number
37            current_value = (current_value << 1) | node.val
38          
39            # If this is a leaf node, return the accumulated binary value
40            if node.left is None and node.right is None:
41                return current_value
42          
43            # Recursively calculate sum for left and right subtrees
44            left_sum = dfs(node.left, current_value)
45            right_sum = dfs(node.right, current_value)
46          
47            return left_sum + right_sum
48      
49        # Start DFS from root with initial binary value of 0
50        return dfs(root, 0)
51
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 root-to-leaf binary numbers in the tree.
19     * Each root-to-leaf path represents a binary number.
20     * 
21     * @param root The root of the binary tree
22     * @return The sum of all root-to-leaf binary numbers
23     */
24    public int sumRootToLeaf(TreeNode root) {
25        return dfs(root, 0);
26    }
27
28    /**
29     * Performs depth-first search to calculate binary numbers from root to leaves.
30     * Builds the binary number by shifting left and adding current node's value.
31     * 
32     * @param node The current node being processed
33     * @param currentBinaryValue The binary value accumulated from root to current node
34     * @return The sum of all binary numbers in the subtree rooted at current node
35     */
36    private int dfs(TreeNode node, int currentBinaryValue) {
37        // Base case: if node is null, contribute 0 to the sum
38        if (node == null) {
39            return 0;
40        }
41      
42        // Build the binary number: shift left by 1 bit and add current node's value
43        // This is equivalent to: currentBinaryValue * 2 + node.val
44        currentBinaryValue = (currentBinaryValue << 1) | node.val;
45      
46        // If this is a leaf node, return the complete binary number
47        if (node.left == null && node.right == null) {
48            return currentBinaryValue;
49        }
50      
51        // Recursively calculate sum for left and right subtrees
52        return dfs(node.left, currentBinaryValue) + dfs(node.right, currentBinaryValue);
53    }
54}
55
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    /**
15     * Calculates the sum of all root-to-leaf paths where each path represents a binary number.
16     * @param root: The root node of the binary tree
17     * @return: The sum of all root-to-leaf binary numbers
18     */
19    int sumRootToLeaf(TreeNode* root) {
20        return dfs(root, 0);
21    }
22
23private:
24    /**
25     * Performs depth-first search to calculate the sum of all root-to-leaf paths.
26     * @param node: Current node being processed
27     * @param currentValue: The accumulated binary value from root to current node
28     * @return: Sum of all root-to-leaf paths starting from current node
29     */
30    int dfs(TreeNode* node, int currentValue) {
31        // Base case: if node is null, return 0
32        if (!node) {
33            return 0;
34        }
35      
36        // Update current binary value by shifting left and adding current node's value
37        // This is equivalent to: currentValue = currentValue * 2 + node->val
38        currentValue = (currentValue << 1) | node->val;
39      
40        // If current node is a leaf, return the accumulated binary value
41        if (!node->left && !node->right) {
42            return currentValue;
43        }
44      
45        // Recursively calculate sum for left and right subtrees
46        return dfs(node->left, currentValue) + dfs(node->right, currentValue);
47    }
48};
49
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 root-to-leaf binary numbers in a binary tree.
17 * Each root-to-leaf path represents a binary number where each node contains 0 or 1.
18 * 
19 * @param root - The root node of the binary tree
20 * @returns The sum of all root-to-leaf binary numbers
21 */
22function sumRootToLeaf(root: TreeNode | null): number {
23    /**
24     * Depth-first search helper function to traverse the tree and calculate binary numbers.
25     * 
26     * @param node - The current node being processed
27     * @param currentBinaryNumber - The accumulated binary number from root to current node
28     * @returns The sum of all binary numbers in the subtree rooted at the current node
29     */
30    const depthFirstSearch = (node: TreeNode | null, currentBinaryNumber: number): number => {
31        // Base case: if node is null, contribute 0 to the sum
32        if (node === null) {
33            return 0;
34        }
35      
36        // Destructure node properties for cleaner access
37        const { val: nodeValue, left: leftChild, right: rightChild } = node;
38      
39        // Build the binary number by shifting left and adding current node's value
40        // This is equivalent to: currentBinaryNumber * 2 + nodeValue
41        currentBinaryNumber = (currentBinaryNumber << 1) | nodeValue;
42      
43        // If this is a leaf node, return the accumulated binary number
44        if (leftChild === null && rightChild === null) {
45            return currentBinaryNumber;
46        }
47      
48        // Recursively calculate sum for left and right subtrees
49        return depthFirstSearch(leftChild, currentBinaryNumber) + 
50               depthFirstSearch(rightChild, currentBinaryNumber);
51    };
52  
53    // Start DFS from root with initial binary number as 0
54    return depthFirstSearch(root, 0);
55}
56

Time and Space Complexity

Time Complexity: O(n)

The algorithm performs a depth-first search (DFS) traversal of the binary tree. Each node in the tree is visited exactly once. During each visit, the algorithm performs constant time operations: bit shifting (t << 1), bitwise OR | root.val, and checking if a node is a leaf. Since we visit all 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 recursion call stack. In the worst case, the tree is completely unbalanced (e.g., a skewed tree where each node has only one child), resulting in a recursion depth equal to the number of nodes n. This would require 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 we consider the worst-case scenario for space complexity analysis. Therefore, the space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrectly Handling Single-Child Nodes

A frequent mistake is assuming that the recursive calls will naturally handle nodes with only one child, but then accidentally treating single-child nodes as leaf nodes.

Incorrect Implementation:

def dfs(node, current_value):
    if node is None:
        return 0
  
    current_value = (current_value << 1) | node.val
  
    # WRONG: This would treat single-child nodes as leaves
    if node.left is None or node.right is None:
        return current_value
  
    return dfs(node.left, current_value) + dfs(node.right, current_value)

Why it's wrong: A node with only one child is NOT a leaf node. The path must continue to the actual leaf.

Correct Approach:

def dfs(node, current_value):
    if node is None:
        return 0
  
    current_value = (current_value << 1) | node.val
  
    # Only return value if BOTH children are None
    if node.left is None and node.right is None:
        return current_value
  
    return dfs(node.left, current_value) + dfs(node.right, current_value)

2. Using Addition Instead of Bitwise OR

Another common error is using addition (+) instead of bitwise OR (|) when appending the current node's value.

Incorrect Implementation:

# WRONG: This could give incorrect results
current_value = (current_value << 1) + node.val

Why it matters: While this often produces the same result (since we're adding 0 or 1 to an even number after left shift), using bitwise OR is:

  • More semantically correct for bit manipulation
  • Potentially more efficient
  • Clearer in expressing the intent of setting a specific bit

Correct Approach:

# Use bitwise OR to set the least significant bit
current_value = (current_value << 1) | node.val

3. Accumulating the Sum Incorrectly

Some implementations try to use a class variable or pass a mutable object to accumulate the sum, which can lead to complexity and potential bugs.

Problematic Implementation:

class Solution:
    def sumRootToLeaf(self, root):
        self.total = 0  # Class variable approach
      
        def dfs(node, current_value):
            if node is None:
                return
          
            current_value = (current_value << 1) | node.val
          
            if node.left is None and node.right is None:
                self.total += current_value  # Modifying class variable
                return
          
            dfs(node.left, current_value)
            dfs(node.right, current_value)
      
        dfs(root, 0)
        return self.total

Why the recursive return approach is better:

  • No need for mutable state or class variables
  • More functional and easier to reason about
  • Natural accumulation through return values
  • Thread-safe if needed

4. Forgetting to Handle Empty Tree

While the problem typically guarantees at least one node, it's good practice to handle the edge case of an empty tree.

Robust Implementation:

def sumRootToLeaf(self, root):
    if root is None:  # Handle empty tree
        return 0
  
    def dfs(node, current_value):
        # ... rest of the implementation
  
    return dfs(root, 0)
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More