Start learning free

LESSON 303 OF 410 · LINKED LISTS

Flatten Multilevel List II - Iterative O(n) Solution in Java

Practice O(n) Read the full explanation

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 iterative (non-recursive) and run in O(n) time.


STEP 1 OF 12
    Ctrl + Enter to check

    JAVA · LINKED LISTS

    Flatten Multilevel List in Java, explained

    Problem Definition

    Given a multilevel doubly linked list, flatten it into a single-level doubly linked list. The input is the head of the first level of the list, and the output should be the head of the flattened list.

    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]

    Constraints:

    • The number of nodes in the list is in the range [0, 1000].
    • -10^5 <= Node.val <= 10^5

    Example:

    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]
    

    Understanding the Problem

    The core challenge is to traverse a multilevel doubly linked list and flatten it into a single-level list. This involves handling the child pointers and ensuring that the order of nodes is preserved as per the depth-first traversal.

    Common applications include flattening hierarchical data structures for easier processing or visualization.

    Potential pitfalls include not correctly handling the child pointers or losing track of the next pointers during traversal.

    Approach

    To solve this problem, we can use an iterative approach with a stack to simulate the depth-first traversal. This avoids the potential pitfalls of recursion, such as stack overflow for deep lists.

    Naive Solution

    A naive solution might involve recursively traversing the list, but this is not optimal due to potential stack overflow issues and higher memory usage.

    Optimized Solution

    An optimized solution uses an iterative approach with a stack:

    • Initialize a stack and push the head of the list onto it.
    • While the stack is not empty, pop a node from the stack.
    • Process the node: if it has a child, push the next node onto the stack (if it exists), then push the child node onto the stack.
    • Update the next and previous pointers to flatten the list.

    Algorithm

    Here is a step-by-step breakdown of the algorithm:

    1. Initialize a stack and push the head of the list onto it.
    2. While the stack is not empty:
      • Pop a node from the stack.
      • If the node has a next node, push it onto the stack.
      • If the node has a child node, push it onto the stack and set the child pointer to null.
      • Update the next and previous pointers to flatten the list.

    Code Implementation

    // Definition for a Node.
    class Node {
        public int val;
        public Node prev;
        public Node next;
        public Node child;
    }
    
    public class Solution {
        public Node flatten(Node head) {
            if (head == null) return head;
    
            // Create a stack to store nodes
            Stack<Node> stack = new Stack<>();
            stack.push(head);
    
            // Pointer to keep track of the previous node
            Node prev = null;
    
            while (!stack.isEmpty()) {
                Node curr = stack.pop();
    
                // If there was a previous node, link it with the current node
                if (prev != null) {
                    prev.next = curr;
                    curr.prev = prev;
                }
    
                // If there is a next node, push it onto the stack
                if (curr.next != null) {
                    stack.push(curr.next);
                }
    
                // If there is a child node, push it onto the stack
                if (curr.child != null) {
                    stack.push(curr.child);
                    // Don't forget to remove the child pointer
                    curr.child = null;
                }
    
                // Move the prev pointer to the current node
                prev = curr;
            }
    
            return head;
        }
    }
    

    Complexity Analysis

    The time complexity of this approach is O(n), where n is the number of nodes in the list. This is because each node is processed exactly once.

    The space complexity is O(n) in the worst case, due to the stack storing all nodes in the list.

    Edge Cases

    Consider the following edge cases:

    • An empty list (head = null) should return an empty list.
    • A list with no child pointers should remain unchanged.
    • A list where all nodes have child pointers should be flattened correctly.

    Testing

    To test the solution comprehensively, consider the following test cases:

    • Simple lists with no child pointers.
    • Lists with varying levels of child pointers.
    • Edge cases such as empty lists and single-node lists.

    Use a testing framework like JUnit to automate the testing process.

    Thinking and Problem-Solving Tips

    When approaching such problems, consider the following tips:

    • Break down the problem into smaller, manageable parts.
    • Use data structures like stacks or queues to simulate recursion iteratively.
    • Think about edge cases and how to handle them.
    • Practice similar problems to improve problem-solving skills.

    Conclusion

    Flattening a multilevel doubly linked list is a common problem that tests your understanding of linked lists and iterative algorithms. By using a stack, we can efficiently flatten the list in O(n) time.

    Understanding and solving such problems is crucial for improving your algorithmic thinking and coding skills.

    Additional Resources

    For further reading and practice, consider the following resources:

    Keep going with JavaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free