Flatten Multilevel List in O(n) Time Using JavaScript


You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.

Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.

 

Example 1:

Input: head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
Output: [1,2,3,7,8,11,12,9,10,4,5,6]
Explanation:

The multilevel linked list in the input is as follows:



After flattening the multilevel linked list it becomes:


Example 2:

Input: head = [1,2,null,3]
Output: [1,3,2]
Explanation:

The input multilevel linked list is as follows:

  1---2---NULL
  |
  3---NULL

Example 3:

Input: head = []
Output: []

 

How multilevel linked list is represented in test case:

We use the multilevel linked list from Example 1 above:

 1---2---3---4---5---6--NULL
         |
         7---8---9---10--NULL
             |
             11--12--NULL

The serialization of each level is as follows:

[1,2,3,4,5,6,null]
[7,8,9,10,null]
[11,12,null]

To serialize all levels together we will add nulls in each level to signify no node connects to the upper node of the previous level. The serialization becomes:

[1,2,3,4,5,6,null]
[null,null,7,8,9,10,null]
[null,11,12,null]

Merging the serialization of each level and removing trailing nulls we obtain:

[1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]

 


Note:

Your algorithm should be recursive and run in O(n) time.


Understanding the Problem

The core challenge of this problem is to traverse a multilevel doubly linked list and flatten it into a single-level doubly linked list. This involves handling nodes that have child pointers, which point to other doubly linked lists. The significance of this problem lies in its applications in data structures and algorithms, particularly in scenarios where hierarchical data needs to be processed linearly.

Approach

To solve this problem, we can use a recursive approach. The idea is to traverse the list and whenever we encounter a node with a child, we recursively flatten the child list and then merge it with the main list. Here is a step-by-step approach:

  1. Start from the head of the list.
  2. Traverse the list using a pointer.
  3. Whenever a node with a child is encountered, recursively flatten the child list.
  4. Merge the flattened child list with the main list.
  5. Continue traversing the main list until the end.

Algorithm

Here is a detailed breakdown of the algorithm:

  1. Initialize a pointer to the head of the list.
  2. While the pointer is not null, do the following:
    • If the current node has a child, recursively flatten the child list.
    • Merge the flattened child list with the main list:
      • Store the next node of the current node.
      • Set the next pointer of the current node to the head of the flattened child list.
      • Set the previous pointer of the head of the flattened child list to the current node.
      • Traverse to the end of the flattened child list.
      • Set the next pointer of the last node of the flattened child list to the stored next node.
      • If the stored next node is not null, set its previous pointer to the last node of the flattened child list.
    • Move the pointer to the next node.

Code Implementation

Here is the JavaScript code to flatten the multilevel doubly linked list:

/**
 * Definition for a Node.
 * function Node(val, prev, next, child) {
 *    this.val = val;
 *    this.prev = prev;
 *    this.next = next;
 *    this.child = child;
 * };
 */

/**
 * @param {Node} head
 * @return {Node}
 */
var flatten = function(head) {
    // Helper function to flatten the list
    const flattenList = (node) => {
        let current = node;
        let last = null;

        while (current) {
            let next = current.next;

            // If the current node has a child, flatten the child list
            if (current.child) {
                let childLast = flattenList(current.child);

                // Connect current node to the child
                current.next = current.child;
                current.child.prev = current;

                // If next is not null, connect the last node of the child list to next
                if (next) {
                    childLast.next = next;
                    next.prev = childLast;
                }

                // Clear the child pointer
                current.child = null;
                last = childLast;
            } else {
                last = current;
            }

            current = next;
        }

        return last;
    };

    flattenList(head);
    return head;
};

Complexity Analysis

The time complexity of this algorithm is O(n), where n is the total number of nodes in the multilevel linked list. This is because each node is visited exactly once. The space complexity is O(1) if we ignore the recursion stack, otherwise it is O(d) where d is the maximum depth of the multilevel linked list due to the recursion stack.

Edge Cases

Here are some potential edge cases and how the algorithm handles them:

Testing

To test the solution comprehensively, we can use a variety of test cases, from simple to complex. Here are some examples:

// Test case 1: Example 1
let head1 = createMultilevelList([1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]);
console.log(flatten(head1)); // Output: [1,2,3,7,8,11,12,9,10,4,5,6]

// Test case 2: Example 2
let head2 = createMultilevelList([1,2,null,3]);
console.log(flatten(head2)); // Output: [1,3,2]

// Test case 3: Example 3
let head3 = createMultilevelList([]);
console.log(flatten(head3)); // Output: []

// Helper function to create a multilevel list from an array
function createMultilevelList(arr) {
    // Implementation of this function is omitted for brevity
}

Thinking and Problem-Solving Tips

When approaching such problems, it is important to break down the problem into smaller parts and think recursively. Here are some tips:

Conclusion

In this blog post, we discussed how to flatten a multilevel doubly linked list using a recursive approach in JavaScript. We covered the problem definition, approach, algorithm, code implementation, complexity analysis, edge cases, and testing. Understanding and solving such problems is important for developing problem-solving skills and improving algorithmic thinking.

Additional Resources

Here are some additional resources for further reading and practice: