364. Nested List Weight Sum II 🔒
Problem Description
You are given a nested list of integers where each element can be either:
- An integer
- A list containing integers or other lists (which can be nested further)
The depth of an integer is determined by how many lists it is nested inside. For example, in the nested list [1,[2,2],[[3],2],1]
:
- The first
1
and last1
are at depth 1 (inside one list) - The two
2
s in[2,2]
are at depth 2 (inside two lists) - The
3
in[[3],2]
is at depth 3 (inside three lists) - The
2
in[[3],2]
is at depth 2 (inside two lists)
The weight of an integer is calculated using the formula: weight = maxDepth - depth + 1
, where maxDepth
is the maximum depth of any integer in the entire nested list.
Your task is to calculate the sum of all integers in the nested list, where each integer is multiplied by its weight.
For example, if maxDepth = 3
for the list [1,[2,2],[[3],2],1]
:
- The integers at depth 1 have weight =
3 - 1 + 1 = 3
- The integers at depth 2 have weight =
3 - 2 + 1 = 2
- The integers at depth 3 have weight =
3 - 3 + 1 = 1
The final result would be: 1×3 + 2×2 + 2×2 + 3×1 + 2×2 + 1×3 = 3 + 4 + 4 + 3 + 4 + 3 = 21
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 nested list structure can be viewed as a graph/tree where each list is a node that can contain integers (leaf nodes) or other lists (child nodes). This hierarchical structure represents a tree-like graph.
Is it a tree?
- Yes: The nested list structure is indeed a tree. Each nested list represents a node in the tree, with integers as leaf nodes and sublists as internal nodes. There's a clear parent-child relationship without cycles, making it a tree structure.
DFS
- Yes: We arrive at DFS as our algorithm choice.
Conclusion: The flowchart suggests using a Depth-First Search (DFS) approach for this problem.
This makes perfect sense because:
- We need to traverse through all levels of nesting to find the maximum depth
- We need to visit every integer in the nested structure while tracking its depth
- DFS naturally handles the recursive nature of nested lists, allowing us to dive deep into each nested level before backtracking
- The depth tracking during DFS traversal directly corresponds to counting how many lists each integer is nested within
The DFS approach allows us to:
- Calculate the depth of each integer as we traverse
- Keep track of the maximum depth encountered
- Accumulate the sum of integers and their weighted sums in a single pass
- Handle the recursive structure of nested lists naturally
Intuition
The key insight is recognizing that calculating weights directly would require two passes through the nested list: first to find the maximum depth, then to calculate the weighted sum. But we can be clever about this.
Let's think about what we're actually computing. For each integer with value v
at depth d
, its contribution to the final sum is v × (maxDepth - d + 1)
. If we expand this:
v × (maxDepth - d + 1)
v × maxDepth - v × d + v
If we sum this across all integers, we get:
(sum of all v × maxDepth) - (sum of all v × d) + (sum of all v)
maxDepth × (sum of all v) - (sum of all v × d) + (sum of all v)
(maxDepth + 1) × (sum of all v) - (sum of all v × d)
This transformation is brilliant because it separates our calculation into two parts:
s
: The simple sum of all integersws
: The weighted sum where each integer is multiplied by its depth
During a single DFS traversal, we can:
- Track the current depth as we descend into nested lists
- Update the maximum depth whenever we encounter an integer
- Add each integer to our simple sum
s
- Add each integer multiplied by its current depth to our weighted sum
ws
After the traversal, we have everything we need: maxDepth
, s
, and ws
. The final answer is simply (maxDepth + 1) × s - ws
.
This approach transforms what seems like a two-pass problem (find max depth, then calculate weighted sum) into an elegant single-pass solution using mathematical manipulation.
Solution Approach
The implementation uses DFS with three tracking variables: maxDepth
, s
(simple sum), and ws
(weighted sum by depth).
DFS Function Design:
We create a helper function dfs(x, d)
that processes a NestedInteger
object x
at depth d
:
-
Update Maximum Depth: First, we update
maxDepth = max(maxDepth, d)
to track the deepest level we've encountered. -
Handle Base Case (Integer): If
x.isInteger()
returnsTrue
:- Add the integer value to our simple sum:
s += x.getInteger()
- Add the depth-weighted value to our weighted sum:
ws += x.getInteger() * d
- Add the integer value to our simple sum:
-
Handle Recursive Case (List): If
x
is a list:- Iterate through each element
y
inx.getList()
- Recursively call
dfs(y, d + 1)
to process each element at the next depth level
- Iterate through each element
Main Algorithm:
-
Initialize our tracking variables:
maxDepth = s = ws = 0
-
Process the input
nestedList
:- For each top-level element
x
innestedList
- Call
dfs(x, 1)
starting at depth 1
- For each top-level element
-
Calculate the final result using our mathematical formula:
- Return
(maxDepth + 1) * s - ws
- Return
Why This Works:
The algorithm leverages the mathematical transformation we derived:
- Instead of calculating
weight = maxDepth - depth + 1
for each integer after findingmaxDepth
- We collect
s
(sum of all integers) andws
(sum of integers × their depths) in one pass - Then apply the formula
(maxDepth + 1) × s - ws
to get the same result
Time and Space Complexity:
- Time:
O(n)
wheren
is the total number of elements (integers and lists) in the nested structure - Space:
O(d)
whered
is the maximum depth, for the recursion call stack
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 the solution with the nested list [[1,1],2,[1,1]]
.
Step 1: Visualize the Structure
[[1,1],2,[1,1]] ├── [1,1] (depth 1) │ ├── 1 (depth 2) │ └── 1 (depth 2) ├── 2 (depth 1) └── [1,1] (depth 1) ├── 1 (depth 2) └── 1 (depth 2)
Step 2: DFS Traversal
Initialize: maxDepth = 0
, s = 0
, ws = 0
Process first element [1,1]
at depth 1:
- It's a list, so recurse into its elements
- Process first
1
at depth 2:- Update:
maxDepth = 2
- Add to simple sum:
s = 0 + 1 = 1
- Add to weighted sum:
ws = 0 + (1 × 2) = 2
- Update:
- Process second
1
at depth 2:maxDepth
remains 2- Update:
s = 1 + 1 = 2
- Update:
ws = 2 + (1 × 2) = 4
Process second element 2
at depth 1:
- It's an integer
maxDepth
remains 2- Update:
s = 2 + 2 = 4
- Update:
ws = 4 + (2 × 1) = 6
Process third element [1,1]
at depth 1:
- It's a list, so recurse into its elements
- Process first
1
at depth 2:maxDepth
remains 2- Update:
s = 4 + 1 = 5
- Update:
ws = 6 + (1 × 2) = 8
- Process second
1
at depth 2:maxDepth
remains 2- Update:
s = 5 + 1 = 6
- Update:
ws = 8 + (1 × 2) = 10
Step 3: Apply the Formula
After traversal, we have:
maxDepth = 2
s = 6
(sum of all integers: 1+1+2+1+1)ws = 10
(weighted sum by depth: 1×2 + 1×2 + 2×1 + 1×2 + 1×2)
Final result: (maxDepth + 1) × s - ws = (2 + 1) × 6 - 10 = 18 - 10 = 8
Step 4: Verify with Direct Calculation
Let's verify by calculating weights directly:
- Integers at depth 1 have weight = 2 - 1 + 1 = 2
- Integers at depth 2 have weight = 2 - 2 + 1 = 1
Weighted sum:
- Four integers (1,1,1,1) at depth 2: 4 × 1 = 4
- One integer (2) at depth 1: 2 × 2 = 4
- Total: 4 + 4 = 8 ✓
The formula correctly transforms the two-pass weight calculation into a single-pass algorithm by collecting the simple sum and depth-weighted sum during traversal.
Solution Implementation
1class Solution:
2 def depthSumInverse(self, nestedList: List[NestedInteger]) -> int:
3 """
4 Calculate the inverse depth sum of a nested list.
5 Each integer is multiplied by its inverse depth weight.
6
7 Args:
8 nestedList: A list of NestedInteger objects
9
10 Returns:
11 The sum where each integer is weighted by (max_depth - current_depth + 1)
12 """
13
14 def traverse_nested_list(nested_integer, current_depth):
15 """
16 Recursively traverse the nested structure to calculate sums and find max depth.
17
18 Args:
19 nested_integer: Current NestedInteger object to process
20 current_depth: Current depth level (starting from 1)
21 """
22 nonlocal max_depth, total_sum, weighted_sum_by_depth
23
24 # Update the maximum depth encountered
25 max_depth = max(max_depth, current_depth)
26
27 if nested_integer.isInteger():
28 # If it's a single integer, add it to our sums
29 integer_value = nested_integer.getInteger()
30 total_sum += integer_value # Sum of all integers
31 weighted_sum_by_depth += integer_value * current_depth # Sum weighted by depth
32 else:
33 # If it's a list, recursively process each element at the next depth level
34 for nested_element in nested_integer.getList():
35 traverse_nested_list(nested_element, current_depth + 1)
36
37 # Initialize tracking variables
38 max_depth = 0 # Maximum depth found in the nested structure
39 total_sum = 0 # Sum of all integers regardless of depth
40 weighted_sum_by_depth = 0 # Sum of integers weighted by their depth
41
42 # Process each top-level element in the nested list
43 for nested_element in nestedList:
44 traverse_nested_list(nested_element, 1)
45
46 # Calculate inverse depth sum using the formula:
47 # For each integer v at depth d: contribution = v * (max_depth - d + 1)
48 # Total = Σ(v * (max_depth - d + 1)) = (max_depth + 1) * Σv - Σ(v * d)
49 return (max_depth + 1) * total_sum - weighted_sum_by_depth
50
1/**
2 * // This is the interface that allows for creating nested lists.
3 * // You should not implement it, or speculate about its implementation
4 * public interface NestedInteger {
5 * // Constructor initializes an empty nested list.
6 * public NestedInteger();
7 *
8 * // Constructor initializes a single integer.
9 * public NestedInteger(int value);
10 *
11 * // @return true if this NestedInteger holds a single integer, rather than a nested list.
12 * public boolean isInteger();
13 *
14 * // @return the single integer that this NestedInteger holds, if it holds a single integer
15 * // Return null if this NestedInteger holds a nested list
16 * public Integer getInteger();
17 *
18 * // Set this NestedInteger to hold a single integer.
19 * public void setInteger(int value);
20 *
21 * // Set this NestedInteger to hold a nested list and adds a nested integer to it.
22 * public void add(NestedInteger ni);
23 *
24 * // @return the nested list that this NestedInteger holds, if it holds a nested list
25 * // Return empty list if this NestedInteger holds a single integer
26 * public List<NestedInteger> getList();
27 * }
28 */
29class Solution {
30 // Maximum depth encountered during traversal
31 private int maxDepth;
32
33 // Weighted sum: sum of (value * depth) for all integers
34 private int weightedSum;
35
36 // Simple sum: sum of all integer values (without weights)
37 private int simpleSum;
38
39 /**
40 * Calculates the inverse depth sum of nested integers.
41 * Each integer is multiplied by its inverse depth weight.
42 * The inverse depth weight is (maxDepth - currentDepth + 1).
43 *
44 * Formula used: (maxDepth + 1) * simpleSum - weightedSum
45 * This formula efficiently computes the inverse depth sum without a second pass.
46 *
47 * @param nestedList List of nested integers to process
48 * @return The inverse depth sum of all integers in the nested structure
49 */
50 public int depthSumInverse(List<NestedInteger> nestedList) {
51 // Process each element in the input list starting at depth 1
52 for (NestedInteger nestedInteger : nestedList) {
53 dfs(nestedInteger, 1);
54 }
55
56 // Calculate inverse depth sum using the mathematical transformation
57 // This avoids needing a second traversal to apply inverse weights
58 return (maxDepth + 1) * simpleSum - weightedSum;
59 }
60
61 /**
62 * Depth-first search to traverse the nested structure.
63 * Accumulates both weighted and simple sums while tracking maximum depth.
64 *
65 * @param nestedInteger Current nested integer being processed
66 * @param currentDepth Current depth in the nested structure (1-indexed)
67 */
68 private void dfs(NestedInteger nestedInteger, int currentDepth) {
69 // Update the maximum depth encountered
70 maxDepth = Math.max(maxDepth, currentDepth);
71
72 if (nestedInteger.isInteger()) {
73 // Process integer value: add to both weighted and simple sums
74 int value = nestedInteger.getInteger();
75 weightedSum += value * currentDepth;
76 simpleSum += value;
77 } else {
78 // Process nested list: recursively traverse each element
79 for (NestedInteger childNestedInteger : nestedInteger.getList()) {
80 dfs(childNestedInteger, currentDepth + 1);
81 }
82 }
83 }
84}
85
1/**
2 * // This is the interface that allows for creating nested lists.
3 * // You should not implement it, or speculate about its implementation
4 * class NestedInteger {
5 * public:
6 * // Constructor initializes an empty nested list.
7 * NestedInteger();
8 *
9 * // Constructor initializes a single integer.
10 * NestedInteger(int value);
11 *
12 * // Return true if this NestedInteger holds a single integer, rather than a nested list.
13 * bool isInteger() const;
14 *
15 * // Return the single integer that this NestedInteger holds, if it holds a single integer
16 * // The result is undefined if this NestedInteger holds a nested list
17 * int getInteger() const;
18 *
19 * // Set this NestedInteger to hold a single integer.
20 * void setInteger(int value);
21 *
22 * // Set this NestedInteger to hold a nested list and adds a nested integer to it.
23 * void add(const NestedInteger &ni);
24 *
25 * // Return the nested list that this NestedInteger holds, if it holds a nested list
26 * // The result is undefined if this NestedInteger holds a single integer
27 * const vector<NestedInteger> &getList() const;
28 * };
29 */
30class Solution {
31public:
32 /**
33 * Calculate the inverse depth sum of nested integers
34 * Each integer is multiplied by its inverse depth (maxDepth - depth + 1)
35 *
36 * @param nestedList: List of nested integers to process
37 * @return: The inverse depth weighted sum of all integers
38 */
39 int depthSumInverse(vector<NestedInteger>& nestedList) {
40 int maxDepth = 0; // Maximum depth found in the nested structure
41 int weightedSum = 0; // Sum of each integer multiplied by its depth
42 int totalSum = 0; // Sum of all integers (unweighted)
43
44 // DFS function to traverse the nested structure
45 // Parameters: current NestedInteger element and its depth
46 function<void(NestedInteger&, int)> dfs = [&](NestedInteger& element, int depth) {
47 // Update maximum depth encountered
48 maxDepth = max(maxDepth, depth);
49
50 if (element.isInteger()) {
51 // If element is an integer, add to both sums
52 int value = element.getInteger();
53 weightedSum += value * depth; // Add value weighted by current depth
54 totalSum += value; // Add unweighted value
55 } else {
56 // If element is a list, recursively process each item
57 for (auto& nestedElement : element.getList()) {
58 dfs(nestedElement, depth + 1);
59 }
60 }
61 };
62
63 // Process each element in the input list starting at depth 1
64 for (auto& element : nestedList) {
65 dfs(element, 1);
66 }
67
68 // Calculate inverse depth sum using the formula:
69 // inverseDepthSum = (maxDepth + 1) * totalSum - weightedSum
70 // This formula converts depth weights to inverse depth weights
71 return (maxDepth + 1) * totalSum - weightedSum;
72 }
73};
74
1/**
2 * // This is the interface that allows for creating nested lists.
3 * // You should not implement it, or speculate about its implementation
4 * class NestedInteger {
5 * If value is provided, then it holds a single integer
6 * Otherwise it holds an empty nested list
7 * constructor(value?: number) {
8 * ...
9 * };
10 *
11 * Return true if this NestedInteger holds a single integer, rather than a nested list.
12 * isInteger(): boolean {
13 * ...
14 * };
15 *
16 * Return the single integer that this NestedInteger holds, if it holds a single integer
17 * Return null if this NestedInteger holds a nested list
18 * getInteger(): number | null {
19 * ...
20 * };
21 *
22 * Set this NestedInteger to hold a single integer equal to value.
23 * setInteger(value: number) {
24 * ...
25 * };
26 *
27 * Set this NestedInteger to hold a nested list and adds a nested integer elem to it.
28 * add(elem: NestedInteger) {
29 * ...
30 * };
31 *
32 * Return the nested list that this NestedInteger holds,
33 * or an empty list if this NestedInteger holds a single integer
34 * getList(): NestedInteger[] {
35 * ...
36 * };
37 * };
38 */
39
40/**
41 * Calculates the inverse depth sum of a nested list structure.
42 * Each integer is multiplied by its inverse depth (maxDepth - currentDepth + 1).
43 *
44 * The algorithm uses a mathematical transformation:
45 * Instead of calculating (maxDepth - depth + 1) * value for each integer,
46 * it computes (maxDepth + 1) * sum - weightedSum
47 * where weightedSum is the sum of each integer multiplied by its actual depth.
48 *
49 * @param nestedList - Array of NestedInteger objects to process
50 * @returns The inverse depth sum of all integers in the nested structure
51 */
52function depthSumInverse(nestedList: NestedInteger[]): number {
53 // Track maximum depth found in the structure
54 let maxDepth: number = 0;
55
56 // Sum of all integers multiplied by their respective depths
57 let weightedSum: number = 0;
58
59 // Simple sum of all integers (without depth weighting)
60 let totalSum: number = 0;
61
62 /**
63 * Depth-first search to traverse the nested structure.
64 * Accumulates weighted sum and total sum while tracking maximum depth.
65 *
66 * @param nestedInteger - Current NestedInteger being processed
67 * @param currentDepth - Current depth level (1-indexed)
68 */
69 const traverseNested = (nestedInteger: NestedInteger, currentDepth: number): void => {
70 // Update the maximum depth encountered
71 maxDepth = Math.max(maxDepth, currentDepth);
72
73 if (nestedInteger.isInteger()) {
74 // Process integer value: add to weighted sum and total sum
75 const integerValue: number = nestedInteger.getInteger()!;
76 weightedSum += integerValue * currentDepth;
77 totalSum += integerValue;
78 } else {
79 // Process nested list: recursively traverse each element
80 const nestedElements: NestedInteger[] = nestedInteger.getList();
81 for (const element of nestedElements) {
82 traverseNested(element, currentDepth + 1);
83 }
84 }
85 };
86
87 // Process each element in the input list starting at depth 1
88 for (const element of nestedList) {
89 traverseNested(element, 1);
90 }
91
92 // Apply the mathematical transformation to get inverse depth sum
93 // This is equivalent to: sum of (maxDepth - depth + 1) * value for each integer
94 return (maxDepth + 1) * totalSum - weightedSum;
95}
96
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs a depth-first search (DFS) traversal through all elements in the nested list structure. Each integer in the nested structure is visited exactly once during the traversal. For each integer encountered, the algorithm performs constant-time operations:
- Updating
maxDepth
with a max comparison - Adding the integer value to the sum
s
- Adding the weighted value to
ws
Since we visit each of the n
integers exactly once and perform O(1)
operations for each, the overall time complexity is O(n)
, where n
is the total number of integers in the nested structure.
Space Complexity: O(d)
The space complexity is determined by the recursive call stack of the DFS function. The maximum depth of recursion equals the maximum nesting depth d
of the input structure. Each recursive call adds a new frame to the call stack, consuming O(1)
space for the function parameters and local variables. Therefore, the worst-case space complexity is O(d)
, where d
is the maximum depth of nesting.
Note: The reference answer states O(n)
for space complexity, which would be accurate if considering the worst-case scenario where the nested structure forms a linear chain (like [[[[[5]]]]]
), making the depth equal to n
. In general practice, the space complexity is more precisely described as O(d)
where d
is the maximum nesting depth.
Common Pitfalls
1. Incorrect Initial Depth Value
One of the most common mistakes is starting the traversal with depth 0 instead of depth 1. The problem states that elements in the outermost list are at depth 1, not depth 0.
Incorrect Implementation:
# Wrong: Starting with depth 0 for nested_element in nestedList: traverse_nested_list(nested_element, 0) # ❌ Should be 1
Correct Implementation:
# Correct: Starting with depth 1 for nested_element in nestedList: traverse_nested_list(nested_element, 1) # ✓
2. Misunderstanding the NestedInteger Interface
Developers often confuse how to handle the NestedInteger interface, particularly when dealing with empty lists or assuming the wrong return types.
Common Mistakes:
# Wrong: Assuming getList() returns integers directly for element in nested_integer.getList(): total_sum += element # ❌ element is NestedInteger, not int # Wrong: Not checking if it's an integer before calling getInteger() value = nested_integer.getInteger() # ❌ May fail if it's a list
Correct Approach:
if nested_integer.isInteger(): value = nested_integer.getInteger() # Process integer value else: for nested_element in nested_integer.getList(): # Process each NestedInteger recursively
3. Formula Application Error
The mathematical formula (maxDepth + 1) * s - ws
might be incorrectly implemented, especially with off-by-one errors.
Incorrect Implementations:
# Wrong formulas that developers might use: return maxDepth * total_sum - weighted_sum_by_depth # ❌ Missing +1 return (maxDepth - 1) * total_sum + weighted_sum_by_depth # ❌ Wrong signs return total_sum * maxDepth - weighted_sum_by_depth + total_sum # ❌ Expanded incorrectly
Correct Formula:
return (max_depth + 1) * total_sum - weighted_sum_by_depth # ✓
4. Variable Scope Issues with Nested Functions
Forgetting to use nonlocal
for variables that need to be modified within the nested function.
Incorrect Implementation:
def depthSumInverse(self, nestedList):
max_depth = 0
total_sum = 0
weighted_sum_by_depth = 0
def traverse_nested_list(nested_integer, current_depth):
# ❌ Without nonlocal, these create new local variables
max_depth = max(max_depth, current_depth) # UnboundLocalError
total_sum += value # UnboundLocalError
Correct Implementation:
def traverse_nested_list(nested_integer, current_depth):
nonlocal max_depth, total_sum, weighted_sum_by_depth # ✓
max_depth = max(max_depth, current_depth)
# ... rest of the function
5. Handling Edge Cases
Not properly handling edge cases like empty lists or deeply nested empty structures.
Potential Issues:
- Empty input list
[]
should return 0 - Nested empty lists like
[[[]]]
should not affect the result - Single integer wrapped in multiple lists
[[[5]]]
should be handled correctly
Robust Solution:
# Handle empty input if not nestedList: return 0 # The existing algorithm naturally handles empty nested lists # since they don't contain integers to add to the sums
Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
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!