2625. Flatten Deeply Nested Array
Problem Description
You are given a multi-dimensional array arr
and a depth value n
. Your task is to flatten this array up to the specified depth level.
A multi-dimensional array is a nested structure that can contain integers or other arrays within it. For example, [1, [2, 3], [[4]]]
is a multi-dimensional array with different levels of nesting.
The flattening operation should remove array brackets and expose the elements inside, but only up to depth n
. The depth counting starts at 0
for elements in the outermost array.
Here's how depth works:
- Elements at the top level have depth
0
- Elements inside one level of nesting have depth
1
- Elements inside two levels of nesting have depth
2
, and so on
When flattening with depth n
:
- If an element's current depth is less than
n
and it's an array, unwrap it - If an element's current depth equals or exceeds
n
, keep it as is - If an element is not an array, keep it as is
For example:
arr = [1, 2, [3, 4, [5, 6]]]
withn = 0
returns[1, 2, [3, 4, [5, 6]]]
(no flattening)arr = [1, 2, [3, 4, [5, 6]]]
withn = 1
returns[1, 2, 3, 4, [5, 6]]
(flatten one level)arr = [1, 2, [3, 4, [5, 6]]]
withn = 2
returns[1, 2, 3, 4, 5, 6]
(flatten two levels)
You must implement this without using JavaScript's built-in Array.flat()
method.
Intuition
The key insight is to think of this problem recursively. When we encounter an array element while iterating through the input, we need to decide whether to unwrap it or keep it as is. This decision depends on how many levels deep we're allowed to flatten (the value of n
).
Think of n
as a "budget" for how many levels we can unwrap. Each time we go one level deeper into nested arrays, we spend one unit from this budget. When n
reaches 0
, we stop unwrapping and keep everything as is.
The recursive approach naturally emerges from this observation:
- If
n = 0
, we've exhausted our flattening budget, so return the array unchanged - Otherwise, iterate through each element:
- If it's an array and we still have budget (
n > 0
), recursively flatten it withn - 1
(using one unit of our budget) - If it's not an array, just add it to our result
- If it's an array and we still have budget (
The spread operator (...
) is perfect here because when we recursively flatten a sub-array, we want to insert all its elements into our result array, not the sub-array itself as a single element.
For example, with [1, [2, [3]]]
and n = 1
:
- Process
1
: not an array, add it →[1]
- Process
[2, [3]]
: it's an array andn = 1
, so recursively flatten withn = 0
- In the recursive call with
n = 0
, return[2, [3]]
unchanged - Spread these elements into our result →
[1, 2, [3]]
- In the recursive call with
This way, each recursive call handles one level of depth, decrementing n
as we go deeper, until we either run out of budget or run out of nested arrays to flatten.
Solution Approach
The implementation uses a recursive approach to flatten the array up to the specified depth. Let's walk through the solution step by step:
Base Case:
if (!n) { return arr; }
When n
is 0
, we've reached our flattening limit. Return the array as-is without any further processing.
Recursive Processing:
const ans: MultiDimensionalArray = [];
for (const x of arr) {
if (Array.isArray(x) && n) {
ans.push(...flat(x, n - 1));
} else {
ans.push(x);
}
}
We create a new result array ans
and iterate through each element x
in the input array:
-
If the element is an array and we have remaining depth (
Array.isArray(x) && n
):- Recursively call
flat(x, n - 1)
to flatten this sub-array with one less depth level - Use the spread operator
...
to push all returned elements intoans
- The
n - 1
decrements our depth budget for the recursive call
- Recursively call
-
If the element is not an array OR we've exhausted our depth:
- Simply push the element as-is into
ans
- This preserves non-array values (numbers) and arrays that shouldn't be flattened further
- Simply push the element as-is into
Example Walkthrough:
For arr = [1, [2, [3, 4]]]
with n = 1
:
- First call:
flat([1, [2, [3, 4]]], 1)
- Process
1
: not an array → push1
to result - Process
[2, [3, 4]]
: is array andn = 1
→ callflat([2, [3, 4]], 0)
- Second call:
flat([2, [3, 4]], 0)
- Since
n = 0
, return[2, [3, 4]]
unchanged
- Since
- Spread
[2, [3, 4]]
into result
- Second call:
- Final result:
[1, 2, [3, 4]]
- Process
The time complexity is O(N)
where N
is the total number of elements across all nesting levels, as we visit each element once. The space complexity is O(D)
where D
is the maximum depth of nesting, due to the recursive call stack.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through a concrete example to understand how the recursive flattening works.
Example: arr = [1, [2, 3], [[4]]]
with n = 1
Initial Call: flat([1, [2, 3], [[4]]], 1)
n = 1
, so we proceed with flattening- Create empty result array:
ans = []
Processing each element:
-
Element
1
:- Not an array → push directly to
ans
ans = [1]
- Not an array → push directly to
-
Element
[2, 3]
:- Is an array AND
n = 1
→ recursively callflat([2, 3], 0)
Recursive Call:
flat([2, 3], 0)
-
n = 0
→ base case reached -
Return
[2, 3]
unchanged -
Back in main call: spread
[2, 3]
intoans
-
ans = [1, 2, 3]
- Is an array AND
-
Element
[[4]]
:- Is an array AND
n = 1
→ recursively callflat([[4]], 0)
Recursive Call:
flat([[4]], 0)
-
n = 0
→ base case reached -
Return
[[4]]
unchanged -
Back in main call: spread
[[4]]
intoans
-
ans = [1, 2, 3, [4]]
- Is an array AND
Final Result: [1, 2, 3, [4]]
Notice how:
- The first-level arrays
[2, 3]
and[[4]]
were unwrapped (their brackets removed) - The second-level array
[4]
remained intact because we only flattened to depth 1 - Each recursive call decremented
n
by 1, controlling how deep we flatten
Solution Implementation
1def flat(arr, n):
2 """
3 Flattens a multi-dimensional array to a specified depth
4
5 Args:
6 arr: The multi-dimensional array to flatten (list containing numbers or nested lists)
7 n: The maximum depth to flatten (0 means no flattening)
8
9 Returns:
10 The flattened array according to the specified depth
11 """
12 # Base case: if n is 0, return the array as-is without flattening
13 if n == 0:
14 return arr
15
16 # Initialize the result list to store flattened elements
17 result = []
18
19 # Iterate through each element in the input array
20 for element in arr:
21 # Check if the current element is a list and we still have depth to flatten
22 if isinstance(element, list) and n > 0:
23 # Recursively flatten the nested list with reduced depth (n - 1)
24 # and extend the result list with the flattened elements
25 result.extend(flat(element, n - 1))
26 else:
27 # If element is not a list or we've reached max depth, add it directly
28 result.append(element)
29
30 return result
31
1import java.util.ArrayList;
2import java.util.List;
3
4/**
5 * Solution class for flattening a multi-dimensional array to a specified depth
6 */
7public class Solution {
8
9 /**
10 * Flattens a multi-dimensional array to a specified depth
11 * @param arr - The multi-dimensional array to flatten (can contain Integer or nested List)
12 * @param n - The maximum depth to flatten (0 means no flattening)
13 * @return The flattened list according to the specified depth
14 */
15 public List<Object> flat(List<Object> arr, int n) {
16 // Base case: if n is 0, return the array as-is without flattening
17 if (n == 0) {
18 return arr;
19 }
20
21 // Initialize the result list to store flattened elements
22 List<Object> result = new ArrayList<>();
23
24 // Iterate through each element in the input array
25 for (Object element : arr) {
26 // Check if the current element is a List and we still have depth to flatten
27 if (element instanceof List && n > 0) {
28 // Recursively flatten the nested list with reduced depth (n - 1)
29 // and add all results into the result list
30 @SuppressWarnings("unchecked")
31 List<Object> nestedList = (List<Object>) element;
32 result.addAll(flat(nestedList, n - 1));
33 } else {
34 // If element is not a List or we've reached max depth, add it directly
35 result.add(element);
36 }
37 }
38
39 return result;
40 }
41}
42
1#include <vector>
2#include <variant>
3#include <memory>
4
5// Forward declaration for recursive type definition
6class MultiDimensionalArray;
7
8// Type definition for elements that can be either numbers or nested arrays
9using ArrayElement = std::variant<int, std::shared_ptr<MultiDimensionalArray>>;
10
11// Class to represent a multi-dimensional array
12class MultiDimensionalArray : public std::vector<ArrayElement> {
13public:
14 using std::vector<ArrayElement>::vector;
15};
16
17/**
18 * Flattens a multi-dimensional array to a specified depth
19 * @param arr - The multi-dimensional array to flatten
20 * @param n - The maximum depth to flatten (0 means no flattening)
21 * @return The flattened array according to the specified depth
22 */
23MultiDimensionalArray flat(const MultiDimensionalArray& arr, int n) {
24 // Base case: if n is 0, return the array as-is without flattening
25 if (n == 0) {
26 return arr;
27 }
28
29 // Initialize the result array to store flattened elements
30 MultiDimensionalArray result;
31
32 // Iterate through each element in the input array
33 for (const auto& element : arr) {
34 // Check if the current element is a nested array and we still have depth to flatten
35 if (std::holds_alternative<std::shared_ptr<MultiDimensionalArray>>(element) && n > 0) {
36 // Get the nested array from the variant
37 auto nestedArray = std::get<std::shared_ptr<MultiDimensionalArray>>(element);
38
39 // Recursively flatten the nested array with reduced depth (n - 1)
40 MultiDimensionalArray flattened = flat(*nestedArray, n - 1);
41
42 // Insert all flattened elements into the result array
43 result.insert(result.end(), flattened.begin(), flattened.end());
44 } else {
45 // If element is not an array or we've reached max depth, add it directly
46 result.push_back(element);
47 }
48 }
49
50 return result;
51}
52
1/**
2 * Type definition for a multi-dimensional array that can contain numbers or nested arrays
3 */
4type MultiDimensionalArray = (number | MultiDimensionalArray)[];
5
6/**
7 * Flattens a multi-dimensional array to a specified depth
8 * @param arr - The multi-dimensional array to flatten
9 * @param n - The maximum depth to flatten (0 means no flattening)
10 * @returns The flattened array according to the specified depth
11 */
12var flat = function (arr: MultiDimensionalArray, n: number): MultiDimensionalArray {
13 // Base case: if n is 0, return the array as-is without flattening
14 if (n === 0) {
15 return arr;
16 }
17
18 // Initialize the result array to store flattened elements
19 const result: MultiDimensionalArray = [];
20
21 // Iterate through each element in the input array
22 for (const element of arr) {
23 // Check if the current element is an array and we still have depth to flatten
24 if (Array.isArray(element) && n > 0) {
25 // Recursively flatten the nested array with reduced depth (n - 1)
26 // and spread the results into the result array
27 result.push(...flat(element, n - 1));
28 } else {
29 // If element is not an array or we've reached max depth, add it directly
30 result.push(element);
31 }
32 }
33
34 return result;
35};
36
Time and Space Complexity
Time Complexity: O(N)
where N
is the total number of elements (including nested elements) in the input array.
The algorithm visits each element in the multi-dimensional array exactly once. Even though the function is recursive and may process arrays at different nesting levels, each element is only processed once when it's encountered. The push(...flat(x, n - 1))
operation spreads the results, but the recursive call itself only processes each element once. Therefore, if there are N
total elements across all nesting levels, the time complexity is linear.
Space Complexity: O(N)
where N
is the total number of elements in the input array.
The space complexity consists of two components:
- Output array space: The
ans
array will eventually contain all elements from the input array (flattened to the specified depth), requiringO(N)
space. - Recursive call stack: In the worst case, when we have deeply nested arrays up to depth
n
, the call stack can grow up toO(n)
levels deep. However, sincen
represents the flattening depth (not the total elements), and is typically much smaller thanN
, this is dominated by the output array space.
The overall space complexity is O(N)
as the output array dominates the space usage.
Common Pitfalls
1. Mutating the Original Array
A common mistake is trying to modify the input array in-place instead of creating a new array. This can lead to unexpected side effects if the original array is used elsewhere in the code.
Incorrect Approach:
def flat(arr, n):
if n == 0:
return arr
i = 0
while i < len(arr):
if isinstance(arr[i], list) and n > 0:
# This modifies the original array!
arr[i:i+1] = flat(arr[i], n - 1)
i += len(arr[i])
else:
i += 1
return arr
Solution: Always create a new result array and avoid modifying the input array.
2. Incorrect Depth Check Logic
Developers often confuse when to stop flattening. A common error is checking n <= 0
after already entering the recursive call, or using the wrong comparison operator.
Incorrect Approach:
def flat(arr, n):
result = []
for element in arr:
# Wrong: should check n > 0, not n >= 0
if isinstance(element, list) and n >= 0:
result.extend(flat(element, n - 1))
else:
result.append(element)
return result
This would flatten one level too deep because when n = 0
, it still attempts to flatten.
Solution: Use the correct condition n > 0
or implement the base case check at the beginning of the function.
3. Not Handling Edge Cases Properly
Failing to handle empty arrays or negative depth values can cause unexpected behavior.
Problematic Scenarios:
# Empty nested arrays might be handled incorrectly flat([[], [[]]], 1) # Should return [[], []] # Negative depth values flat([1, [2, 3]], -1) # Should return [1, [2, 3]] unchanged
Solution: Add proper validation:
def flat(arr, n):
# Handle negative or zero depth
if n <= 0:
return arr
result = []
for element in arr:
if isinstance(element, list):
result.extend(flat(element, n - 1))
else:
result.append(element)
return result
4. Type Checking Confusion
In Python, using type(element) == list
instead of isinstance(element, list)
can cause issues with list subclasses or when dealing with different array-like structures.
Incorrect Approach:
def flat(arr, n):
if n == 0:
return arr
result = []
for element in arr:
# This might miss list subclasses
if type(element) == list and n > 0:
result.extend(flat(element, n - 1))
else:
result.append(element)
return result
Solution: Use isinstance()
for more robust type checking that handles inheritance properly.
5. Forgetting to Decrement Depth in Recursive Call
A critical error is passing the same depth value to recursive calls instead of decrementing it.
Incorrect Approach:
def flat(arr, n):
if n == 0:
return arr
result = []
for element in arr:
if isinstance(element, list):
# Wrong: passing n instead of n - 1
result.extend(flat(element, n))
else:
result.append(element)
return result
This creates infinite recursion for nested arrays when n > 0
.
Solution: Always decrement the depth parameter when making recursive calls: flat(element, n - 1)
.
A heap is a ...?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!