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 positionp
, its:- Left child is at depth
d+1
and position2*p - 1
- Right child is at depth
d+1
and position2*p
- Left child is at depth
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:
- We need to explore all root-to-leaf paths
- We need to maintain the running sum as we traverse down each path
- When we reach a leaf node, we need to add the accumulated path sum to our total
- 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.
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 position2*p - 1
- Its right child would be at depth
d+1
and position2*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
→ key11
(depth 1, position 1), value3
215
→ key21
(depth 2, position 1), value5
221
→ key22
(depth 2, position 2), value1
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 positionp
usingdivmod
- Calculate left child position: depth becomes
d+1
, position becomes2*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 EvaluatorExample 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)
anddfs(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)
anddfs(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:
- The dictionary
mp
which stores alln
elements from the input, requiringO(n)
space - 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 beO(log n)
- The variables
ans
,t
,d
,p
,l
, andr
use constant spaceO(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
, position2*p - 1
- Right child: depth
d+1
, position2*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.
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
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
Binary Tree Min Depth Prereq BFS on Tree problems bfs_intro Given a binary tree find the depth of the shallowest leaf node https assets algo monster binary_tree_min_depth png Explanation We can solve this problem with either DFS or BFS With DFS we traverse the whole tree looking for leaf nodes and record and update the minimum depth as we go With BFS though since we search level by level we are guaranteed to find the shallowest leaf node
Want a Structured Path to Master System Design Too? Don’t Miss This!