Facebook Pixel

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]]] with n = 0 returns [1, 2, [3, 4, [5, 6]]] (no flattening)
  • arr = [1, 2, [3, 4, [5, 6]]] with n = 1 returns [1, 2, 3, 4, [5, 6]] (flatten one level)
  • arr = [1, 2, [3, 4, [5, 6]]] with n = 2 returns [1, 2, 3, 4, 5, 6] (flatten two levels)

You must implement this without using JavaScript's built-in Array.flat() method.

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

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:

  1. If n = 0, we've exhausted our flattening budget, so return the array unchanged
  2. Otherwise, iterate through each element:
    • If it's an array and we still have budget (n > 0), recursively flatten it with n - 1 (using one unit of our budget)
    • If it's not an array, just add it to our result

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 and n = 1, so recursively flatten with n = 0
    • In the recursive call with n = 0, return [2, [3]] unchanged
    • Spread these elements into our result → [1, 2, [3]]

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:

  1. 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 into ans
    • The n - 1 decrements our depth budget for the recursive call
  2. 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

Example Walkthrough:

For arr = [1, [2, [3, 4]]] with n = 1:

  • First call: flat([1, [2, [3, 4]]], 1)
    • Process 1: not an array → push 1 to result
    • Process [2, [3, 4]]: is array and n = 1 → call flat([2, [3, 4]], 0)
      • Second call: flat([2, [3, 4]], 0)
        • Since n = 0, return [2, [3, 4]] unchanged
      • Spread [2, [3, 4]] into result
    • Final result: [1, 2, [3, 4]]

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 Evaluator

Example 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:

  1. Element 1:

    • Not an array → push directly to ans
    • ans = [1]
  2. Element [2, 3]:

    • Is an array AND n = 1 → recursively call flat([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] into ans

    • ans = [1, 2, 3]

  3. Element [[4]]:

    • Is an array AND n = 1 → recursively call flat([[4]], 0)

    Recursive Call: flat([[4]], 0)

    • n = 0 → base case reached

    • Return [[4]] unchanged

    • Back in main call: spread [[4]] into ans

    • ans = [1, 2, 3, [4]]

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:

  1. Output array space: The ans array will eventually contain all elements from the input array (flattened to the specified depth), requiring O(N) space.
  2. Recursive call stack: In the worst case, when we have deeply nested arrays up to depth n, the call stack can grow up to O(n) levels deep. However, since n represents the flattening depth (not the total elements), and is typically much smaller than N, 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).

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

A heap is a ...?


Recommended Readings

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

Load More