Facebook Pixel

666. Path Sum IV 🔒

Problem Description

You are given an array of three-digit integers that represents a binary tree. Each three-digit integer encodes information about a node in the tree:

  • Hundreds digit (d): Represents the depth/level of the node, where 1 ≤ d ≤ 4
  • Tens digit (p): Represents the position of the node within its level, where 1 ≤ p ≤ 8
  • Units digit (v): Represents the value stored in the node, where 0 ≤ v ≤ 9

For example, the integer 235 represents:

  • Depth = 2 (second level)
  • Position = 3 (third position in that level)
  • Value = 5

The position follows the convention of a full binary tree, where positions are numbered from left to right starting at 1 for each level.

The relationship between parent and child nodes follows these rules:

  • For a node at depth d and position p, its:
    • Left child is at depth d+1 and position 2*p - 1
    • Right child is at depth d+1 and position 2*p

Your task is to find the sum of all root-to-leaf paths in this tree. A root-to-leaf path sum is calculated by adding up all the node values along a path from the root to any leaf node. The final answer is the sum of all such path sums.

For instance, if the tree has paths:

  • Root → Leaf1 with values [1, 2, 3], the path sum is 6
  • Root → Leaf2 with values [1, 2, 4], the path sum is 7
  • The total answer would be 6 + 7 = 13

The input array is given in ascending order and is guaranteed to represent a valid connected binary tree.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: Although the problem is presented as an array of integers, it fundamentally represents a binary tree structure. Trees are a special type of graph (specifically, a connected acyclic graph).

Is it a tree?

  • Yes: The problem explicitly states we're working with a binary tree. The array representation encodes parent-child relationships through the depth and position values, where each node can have at most two children (left and right).

DFS

  • This is our conclusion: For a tree structure where we need to traverse from root to leaves and accumulate values along paths, DFS is the appropriate choice.

Why DFS is perfect for this problem:

  1. We need to explore all root-to-leaf paths
  2. We need to maintain the running sum as we traverse down each path
  3. When we reach a leaf node, we need to add the accumulated path sum to our total
  4. DFS naturally handles the backtracking needed to explore all paths

The solution uses DFS to:

  • Start from the root node (depth 1, position 1)
  • Recursively visit children nodes by calculating their positions
  • Accumulate node values along each path
  • When a leaf is reached (no children exist), add the path sum to the final answer

Conclusion: The flowchart correctly guides us to use DFS for this tree-based path sum problem.

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

Intuition

The key insight is recognizing that despite the array representation, we're dealing with a tree traversal problem. The three-digit encoding is just a clever way to store tree nodes without pointers.

Think about what we need: the sum of all root-to-leaf paths. This immediately suggests we need to traverse from the root to every leaf, keeping track of the cumulative sum along the way. When we reach a leaf (a node with no children), we add that path's sum to our total.

The encoding scheme gives us a mathematical relationship between parent and child nodes. For a node at depth d and position p:

  • Its left child would be at depth d+1 and position 2*p - 1
  • Its right child would be at depth d+1 and position 2*p

This means we can "navigate" the tree without actually building it. We can use the depth-position pair as a key to look up nodes.

The solution cleverly uses a hashmap where:

  • Key: depth * 10 + position (the first two digits of the three-digit number)
  • Value: the node's value (the last digit)

For example, 235 becomes key 23 with value 5.

Starting from the root (which is always at depth 1, position 1, giving us key 11), we recursively explore both children. We accumulate the sum as we go down, and when we hit a node that has no children in our hashmap, we know it's a leaf - that's when we add the accumulated sum to our answer.

The beauty of this approach is that we don't need to build the actual tree structure. We just use DFS with the mathematical formula to traverse the "virtual" tree encoded in the array.

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

Solution Approach

The implementation uses DFS with a hashmap to efficiently traverse the encoded tree structure.

Step 1: Build the HashMap

mp = {num // 10: num % 10 for num in nums}

We create a dictionary where:

  • Key: num // 10 extracts the first two digits (depth and position)
  • Value: num % 10 extracts the last digit (node value)

For example, if nums = [113, 215, 221]:

  • 113 → key 11 (depth 1, position 1), value 3
  • 215 → key 21 (depth 2, position 1), value 5
  • 221 → key 22 (depth 2, position 2), value 1

Step 2: DFS Traversal The dfs function takes two parameters:

  • node: the current node's encoded position (depth * 10 + position)
  • t: the accumulated sum from root to current node
def dfs(node, t):
    if node not in mp:
        return

First, we check if the node exists in our tree. If not, we return immediately.

Step 3: Calculate Children Positions

t += mp[node]
d, p = divmod(node, 10)
l = (d + 1) * 10 + (p * 2) - 1
r = l + 1
  • Add current node's value to the running sum
  • Extract depth d and position p using divmod
  • Calculate left child position: depth becomes d+1, position becomes 2*p - 1
  • Right child is simply left child position plus 1

For example, if current node is 21 (depth 2, position 1):

  • Left child: (2+1)*10 + (1*2) - 1 = 31
  • Right child: 32

Step 4: Check for Leaf Nodes

if l not in mp and r not in mp:
    ans += t
    return

If neither left nor right child exists in the hashmap, we've reached a leaf node. We add the accumulated path sum to our answer.

Step 5: Recursive Calls

dfs(l, t)
dfs(r, t)

If it's not a leaf, recursively explore both children with the current accumulated sum.

Step 6: Start DFS from Root

dfs(11, 0)

The root is always at depth 1, position 1, giving us key 11. We start with an accumulated sum of 0.

The algorithm visits each node exactly once, making the time complexity O(n) where n is the number of nodes. The space complexity is also O(n) for the hashmap storage.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a small example with the input: nums = [113, 125, 221, 342, 348]

Step 1: Decode and Build HashMap

First, let's decode what each number represents:

  • 113: depth=1, position=1, value=3 (root node)
  • 125: depth=1, position=2, value=5 (invalid - only one root at depth 1, position 1)
  • 221: depth=2, position=2, value=1 (right child of root)
  • 342: depth=3, position=4, value=2 (right child of node at 2,2)
  • 348: depth=3, position=4, value=8 (duplicate position - invalid)

Wait, let me use a valid example: nums = [113, 221, 342]

  • 113: depth=1, position=1, value=3 (root)
  • 221: depth=2, position=2, value=1 (right child of root)
  • 342: depth=3, position=4, value=2 (right child of 221)

Building the hashmap:

mp = {11: 3, 22: 1, 34: 2}

Step 2: Start DFS from Root

Call dfs(11, 0):

  • Node 11 exists in mp
  • Add value: t = 0 + 3 = 3
  • Calculate children:
    • d = 1, p = 1
    • Left child: (1+1)*10 + (1*2-1) = 21
    • Right child: 22
  • Check if leaf: 21 not in mp, but 22 is in mp → not a leaf
  • Recursively call dfs(21, 3) and dfs(22, 3)

Step 3: Explore Left Path

Call dfs(21, 3):

  • Node 21 not in mp → return immediately

Step 4: Explore Right Path

Call dfs(22, 3):

  • Node 22 exists in mp
  • Add value: t = 3 + 1 = 4
  • Calculate children:
    • d = 2, p = 2
    • Left child: (2+1)*10 + (2*2-1) = 33
    • Right child: 34
  • Check if leaf: 33 not in mp, 34 is in mp → not a leaf
  • Recursively call dfs(33, 4) and dfs(34, 4)

Step 5: Continue Right Path

Call dfs(33, 4):

  • Node 33 not in mp → return immediately

Call dfs(34, 4):

  • Node 34 exists in mp
  • Add value: t = 4 + 2 = 6
  • Calculate children:
    • d = 3, p = 4
    • Left child: (3+1)*10 + (4*2-1) = 47
    • Right child: 48
  • Check if leaf: neither 47 nor 48 in mp → THIS IS A LEAF!
  • Add to answer: ans = 0 + 6 = 6

Final Result

The tree has only one root-to-leaf path: 3 → 1 → 2 with sum = 6

The visual tree structure:

      3 (root at 1,1)
       \
        1 (at 2,2)
         \
          2 (at 3,4)

Total answer = 6

Solution Implementation

1class Solution:
2    def pathSum(self, nums: List[int]) -> int:
3        """
4        Calculate the sum of all root-to-leaf paths in a binary tree.
5        The tree is represented as a list where each element encodes:
6        - First digit: depth (1-indexed)
7        - Second digit: position in that depth (1-indexed, left to right)
8        - Third digit: node value
9        """
10      
11        def dfs(node_key: int, current_sum: int) -> None:
12            """
13            Perform depth-first search to find all root-to-leaf paths.
14          
15            Args:
16                node_key: Two-digit key representing depth and position
17                current_sum: Sum accumulated from root to current node
18            """
19            # If current node doesn't exist, return
20            if node_key not in node_map:
21                return
22          
23            # Add current node's value to the running sum
24            current_sum += node_map[node_key]
25          
26            # Extract depth and position from the node key
27            depth, position = divmod(node_key, 10)
28          
29            # Calculate keys for left and right children
30            # Left child: next depth, position (2*p - 1)
31            left_child_key = (depth + 1) * 10 + (position * 2) - 1
32            # Right child: next depth, position (2*p)
33            right_child_key = left_child_key + 1
34          
35            # Access the outer scope variable
36            nonlocal total_path_sum
37          
38            # If current node is a leaf (no children exist)
39            if left_child_key not in node_map and right_child_key not in node_map:
40                total_path_sum += current_sum
41                return
42          
43            # Recursively explore left and right subtrees
44            dfs(left_child_key, current_sum)
45            dfs(right_child_key, current_sum)
46      
47        # Initialize total sum of all paths
48        total_path_sum = 0
49      
50        # Create mapping: node_key (depth*10 + position) -> node_value
51        # Extract first two digits as key, last digit as value
52        node_map = {num // 10: num % 10 for num in nums}
53      
54        # Start DFS from root (depth=1, position=1, key=11)
55        dfs(11, 0)
56      
57        return total_path_sum
58
1class Solution {
2    private int totalSum;
3    private Map<Integer, Integer> nodeValueMap;
4
5    /**
6     * Calculate the sum of all root-to-leaf paths in a binary tree represented as an array.
7     * Each element in nums is a 3-digit number where:
8     * - First digit: depth (level) of the node
9     * - Second digit: position in that level
10     * - Third digit: value of the node
11     * 
12     * @param nums Array of 3-digit numbers representing tree nodes
13     * @return Sum of all root-to-leaf path sums
14     */
15    public int pathSum(int[] nums) {
16        totalSum = 0;
17        nodeValueMap = new HashMap<>(nums.length);
18      
19        // Parse each number and store in map
20        // Key: depth*10 + position (first two digits)
21        // Value: node value (last digit)
22        for (int num : nums) {
23            int nodeKey = num / 10;  // Extract depth and position
24            int nodeValue = num % 10; // Extract node value
25            nodeValueMap.put(nodeKey, nodeValue);
26        }
27      
28        // Start DFS from root node (depth=1, position=1, represented as 11)
29        dfs(11, 0);
30      
31        return totalSum;
32    }
33
34    /**
35     * Depth-first search to traverse the tree and calculate path sums.
36     * 
37     * @param nodeKey Current node identifier (depth*10 + position)
38     * @param currentPathSum Sum accumulated from root to current node's parent
39     */
40    private void dfs(int nodeKey, int currentPathSum) {
41        // Base case: node doesn't exist
42        if (!nodeValueMap.containsKey(nodeKey)) {
43            return;
44        }
45      
46        // Add current node's value to the path sum
47        currentPathSum += nodeValueMap.get(nodeKey);
48      
49        // Extract depth and position from nodeKey
50        int depth = nodeKey / 10;
51        int position = nodeKey % 10;
52      
53        // Calculate left and right child keys
54        // Left child: next depth, position = (parent_position * 2) - 1
55        int leftChildKey = (depth + 1) * 10 + (position * 2) - 1;
56        // Right child: next depth, position = parent_position * 2
57        int rightChildKey = leftChildKey + 1;
58      
59        // Check if current node is a leaf (no children exist)
60        if (!nodeValueMap.containsKey(leftChildKey) && !nodeValueMap.containsKey(rightChildKey)) {
61            // Leaf node found, add the complete path sum to total
62            totalSum += currentPathSum;
63            return;
64        }
65      
66        // Recursively traverse left and right subtrees
67        dfs(leftChildKey, currentPathSum);
68        dfs(rightChildKey, currentPathSum);
69    }
70}
71
1class Solution {
2public:
3    int totalSum;
4    unordered_map<int, int> nodeValues;  // Map from node position to node value
5
6    int pathSum(vector<int>& nums) {
7        // Initialize variables
8        totalSum = 0;
9        nodeValues.clear();
10      
11        // Parse input array and build the tree representation
12        // Each number is formatted as XYZ where X is depth, Y is position, Z is value
13        for (int num : nums) {
14            int position = num / 10;  // Extract XY (depth and position)
15            int value = num % 10;      // Extract Z (node value)
16            nodeValues[position] = value;
17        }
18      
19        // Start DFS from root node (depth 1, position 1)
20        dfs(11, 0);
21      
22        return totalSum;
23    }
24
25private:
26    void dfs(int nodePosition, int currentPathSum) {
27        // If node doesn't exist, return
28        if (nodeValues.find(nodePosition) == nodeValues.end()) {
29            return;
30        }
31      
32        // Add current node's value to the path sum
33        currentPathSum += nodeValues[nodePosition];
34      
35        // Extract depth and position from node identifier
36        int depth = nodePosition / 10;
37        int position = nodePosition % 10;
38      
39        // Calculate left and right child positions
40        // Left child: depth+1, position*2-1
41        // Right child: depth+1, position*2
42        int leftChildPosition = (depth + 1) * 10 + (position * 2) - 1;
43        int rightChildPosition = leftChildPosition + 1;
44      
45        // If current node is a leaf (no children exist), add path sum to total
46        if (nodeValues.find(leftChildPosition) == nodeValues.end() && 
47            nodeValues.find(rightChildPosition) == nodeValues.end()) {
48            totalSum += currentPathSum;
49            return;
50        }
51      
52        // Continue DFS traversal for existing children
53        dfs(leftChildPosition, currentPathSum);
54        dfs(rightChildPosition, currentPathSum);
55    }
56};
57
1let totalSum: number;
2let nodeValues: Map<number, number>;  // Map from node position to node value
3
4function pathSum(nums: number[]): number {
5    // Initialize variables
6    totalSum = 0;
7    nodeValues = new Map<number, number>();
8  
9    // Parse input array and build the tree representation
10    // Each number is formatted as XYZ where X is depth, Y is position, Z is value
11    for (const num of nums) {
12        const position = Math.floor(num / 10);  // Extract XY (depth and position)
13        const value = num % 10;                 // Extract Z (node value)
14        nodeValues.set(position, value);
15    }
16  
17    // Start DFS from root node (depth 1, position 1)
18    dfs(11, 0);
19  
20    return totalSum;
21}
22
23function dfs(nodePosition: number, currentPathSum: number): void {
24    // If node doesn't exist, return
25    if (!nodeValues.has(nodePosition)) {
26        return;
27    }
28  
29    // Add current node's value to the path sum
30    currentPathSum += nodeValues.get(nodePosition)!;
31  
32    // Extract depth and position from node identifier
33    const depth = Math.floor(nodePosition / 10);
34    const position = nodePosition % 10;
35  
36    // Calculate left and right child positions
37    // Left child: depth+1, position*2-1
38    // Right child: depth+1, position*2
39    const leftChildPosition = (depth + 1) * 10 + (position * 2) - 1;
40    const rightChildPosition = leftChildPosition + 1;
41  
42    // If current node is a leaf (no children exist), add path sum to total
43    if (!nodeValues.has(leftChildPosition) && !nodeValues.has(rightChildPosition)) {
44        totalSum += currentPathSum;
45        return;
46    }
47  
48    // Continue DFS traversal for existing children
49    dfs(leftChildPosition, currentPathSum);
50    dfs(rightChildPosition, currentPathSum);
51}
52

Time and Space Complexity

Time Complexity: O(n) where n is the number of elements in the input list nums.

The algorithm performs a depth-first search (DFS) traversal on a binary tree represented by the input array. Each node in the tree (represented by an element in nums) is visited at most once during the DFS traversal. The dictionary creation mp = {num // 10: num % 10 for num in nums} takes O(n) time to process all elements. The DFS function visits each node exactly once, performing constant time operations at each node (dictionary lookup, arithmetic operations, and recursive calls). Therefore, the overall time complexity is O(n).

Space Complexity: O(n) where n is the number of elements in the input list nums.

The space complexity consists of:

  1. The dictionary mp which stores all n elements from the input, requiring O(n) space
  2. The recursive call stack for DFS, which in the worst case (a skewed tree) can go up to depth O(n), though for a balanced binary tree it would be O(log n)
  3. The variables ans, t, d, p, l, and r use constant space O(1)

The dominant factor is the dictionary storage and potentially the call stack, giving us an overall space complexity of O(n).

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

Common Pitfalls

1. Incorrectly Identifying Leaf Nodes

Pitfall: A common mistake is assuming a node is a leaf only when it has no left child, or checking only one child's existence. This can lead to incorrect path sum calculations.

# WRONG: Only checking left child
if l not in mp:  
    ans += t
    return

# WRONG: Using OR instead of AND
if l not in mp or r not in mp:  
    ans += t
    return

Why it's wrong: In a binary tree, a node is a leaf only when it has NO children at all. A node with just one child (left or right) is not a leaf node.

Solution: Always check that BOTH children are missing:

if l not in mp and r not in mp:
    ans += t
    return

2. Forgetting to Use Nonlocal/Global for Answer Variable

Pitfall: Trying to modify the answer variable inside the nested function without proper scope declaration:

def pathSum(self, nums):
    ans = 0
  
    def dfs(node, t):
        # This will create a local variable 'ans' instead of modifying outer one
        if l not in mp and r not in mp:
            ans += t  # UnboundLocalError!

Solution: Use nonlocal to modify the outer scope variable:

def dfs(node, t):
    nonlocal ans  # Declare we're using the outer scope variable
    if l not in mp and r not in mp:
        ans += t

3. Incorrect Child Position Calculation

Pitfall: Miscalculating the child positions, especially forgetting the offset or using wrong formulas:

# WRONG: Forgetting to subtract 1 for left child
l = (d + 1) * 10 + (p * 2)  # This gives position 2p instead of 2p-1

# WRONG: Using wrong formula entirely
l = d * 10 + (p * 2) - 1  # Wrong depth calculation

Solution: Remember the exact formulas:

  • Left child: depth d+1, position 2*p - 1
  • Right child: depth d+1, position 2*p
l = (d + 1) * 10 + (p * 2) - 1
r = l + 1  # or r = (d + 1) * 10 + (p * 2)

4. Not Handling Single-Node Trees

Pitfall: The code might fail for edge cases like a tree with only a root node:

nums = [111]  # Tree with just root having value 1

Why it matters: A single node is both root and leaf, so its value should be counted as a complete path.

Solution: The current implementation handles this correctly because:

  • The root exists in the map
  • It has no children, so it's identified as a leaf
  • The path sum (just the root value) is added to the answer

5. Assuming Input Array Order

Pitfall: Some might try to traverse the input array directly instead of using the encoding:

# WRONG: Assuming array indices correspond to tree structure
for i in range(len(nums)):
    if is_leaf(i):  # This doesn't work!
        # Process leaf

Solution: Always decode the position information from the three-digit number and use a hashmap for O(1) lookups:

mp = {num // 10: num % 10 for num in nums}

This approach works regardless of the input array order (though the problem states it's sorted) and handles sparse trees where not all positions are filled.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

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

Load More