Exploring Leetcode : Flatten a Multilevel Doubly Linked List

Exploring the Depths of Coding: LeetCode's Flatten a Multilevel Doubly Linked List

·

8 min read

Introduction

Leetcode is a renowned platform that offers coding interviews and challenging algorithmic problems. Among its fascinating problem sets, there's one called "Flatten a Multilevel Doubly Linked List." This particular problem involves working with a doubly linked list where each node can contain a child-linked list, forming a multilevel structure. The objective is to flatten the list, eliminating the nested structure and transforming it into a single-level doubly linked list. Solving this problem requires a strong grasp of linked list manipulation and efficient problem-solving skills. In this discussion, we'll delve into the problem in detail and explore potential approaches to tackle it effectively.

Problem Overview

The Problem Overview for "Flatten a Multilevel Doubly Linked List" can be better understood by referring to the provided illustration on the official LeetCode website. You can access the illustration and detailed description of the problem at the following link: https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/description/

Approach Solution

Before discussing the approach, let's take a look at the code solution.

var flatten = function(head) {
  if (!head) return head;

    let currentNode = head;
    while (currentNode !== null) {
        if (currentNode.child === null) {
            currentNode = currentNode.next;
        } else {
            let tail = currentNode.child;
            while (tail.next !== null) {
                tail = tail.next;
            }

            tail.next = currentNode.next;
            if (tail.next !== null) {
                tail.next.prev = tail;
            }

            currentNode.next = currentNode.child;
            currentNode.next.prev = currentNode;
            currentNode.child = null;
        }
    }

    return head;
};

If you didn't fully understand it, don't worry. Here's an explanation to help clarify things for you.

  1. The flatten function takes the head node of a multilevel doubly linked list as input and returns the flattened list.

  2. The code first checks if the head node is null. If it is, it immediately returns the head itself since there is no list to flatten.

  3. The code uses a while loop to iterate through each node of the list starting from the head.

  4. Inside the loop, it checks if the current node has a child. If it does not have a child, it simply moves to the next node by updating the currentNode variable to currentNode.next.

  5. If the current node has a child, the code enters the else block. It starts by finding the tail of the child list by iterating through the child nodes using a while loop.

  6. Once the tail of the child list is found, the code performs the necessary pointer manipulations to integrate the child list into the main list. It connects the tail of the child list to the next node of the current node, ensuring the order is maintained. It also updates the previous pointers accordingly.

  7. After integrating the child list, the code sets the currentNode.next pointer to the child list. It also updates the previous pointer of the new next node to currentNode.

  8. Finally, the code sets the currentNode.child to null since the child list has been flattened and no longer exists.

  9. The while loop continues until all nodes in the list have been processed.

  10. Finally, the function returns the head of the flattened list.

Test Case

To make you understand solid, you can refer this some test cases

  1. Test Case: Empty List Input: null Expected Output: null

  2. Test Case: Single Node Input: { val: 1, prev: null, next: null, child: null } Expected Output: { val: 1, prev: null, next: null, child: null }

  3. Test Case: Flat List Input: { val: 1, prev: null, next: { val: 2, prev: null, next: { val: 3, prev: null, next: null, child: null }, child: null }, child: null } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 3, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }

  4. Test Case: Multilevel List Input: { val: 1, prev: null, next: { val: 2, prev: null, next: { val: 3, prev: null, next: null, child: null }, child: { val: 4, prev: null, next: { val: 5, prev: null, next: null, child: null }, child: null } }, child: null } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 4, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: { val: 5, prev: { val: 4, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }, child: null }

  5. Test Case: List with a Single Child Input: { val: 1, prev: null, next: { val: 2, prev: null, next: null, child: { val: 3, prev: null, next: null, child: null } }, child: null } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 3, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }

  6. Test Case: List with Multiple Levels of Children Input: { val: 1, prev: null, next: { val: 2, prev: null, next: null, child: { val: 3, prev: null, next: null, child: { val: 4, prev: null, next: null, child: null } } }, child: { val: 5, prev: null, next: null, child: { val: 6, prev: null, next: null, child: null } } } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 5, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: { val: 6, prev: { val: 5, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }, child: { val: 3, prev: null, next: { val: 4, prev: { val: 3, prev: null, next: null, child: null }, next: null, child: null }, child: null } }

  7. Test Case: List with Empty Child Lists Input: { val: 1, prev: null, next: { val: 2, prev: null, next: { val: 3, prev: null, next: null, child: null }, child: { val: 4, prev: null, next: null, child: null } }, child: { val: 5, prev: null, next: null, child: null } } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 4, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }

  8. Test Case: List with No Child but with Prev Pointer Input: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, child: null } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, child: null }

  9. Test Case: List with No Child but with Next and Prev Pointers Input: { val: 1, prev: { val: 0, prev: null, next: { val: 1, prev: null, next: null, child: null }, child: null }, next: { val: 2, prev: { val: 1, prev: { val: 0, prev: null, next: { val: 1, prev: null, next: null, child: null }, child: null }, next: null, child: null }, next: null, child: null }, child: null } Expected Output: { val: 0, prev: null, next: { val: 1, prev: { val: 0, prev: null, next: { val: 1, prev: null, next: null, child: null }, child: null }, next: { val: 2, prev: { val: 1, prev: { val: 0, prev: null, next: { val: 1, prev: null, next: null, child: null }, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }

  10. Test Case: List with Complex Multilevel Structure Input: { val: 1, prev: null, next: { val: 2, prev: null, next: { val: 3, prev: null, next: null, child: null }, child: { val: 4, prev: null, next: { val: 5, prev: null, next: null, child: null }, child: null } }, child: { val: 6, prev: null, next: { val: 7, prev: null, next: null, child: null }, child: { val: 8, prev: null, next: { val: 9, prev: null, next: null, child: null }, child: null } } } Expected Output: { val: 1, prev: null, next: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: { val: 4, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: { val: 5, prev: { val: 4, prev: { val: 2, prev: { val: 1, prev: null, next: null, child: null }, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }, child: { val: 6, prev: null, next: { val: 7, prev: { val: 6, prev: null, next: { val: 8, prev: { val: 7, prev: { val: 6, prev: null, next: { val: 7, prev: null, next: null, child: null }, child: null }, next: null, child: null }, next: { val: 9, prev: { val: 8, prev: { val: 7, prev: { val: 6, prev: null, next: { val: 7, prev: null, next: null, child: null }, child: null }, next: null, child: null }, next: null, child: null }, next: null, child: null }, child: null }, child: null }, next: null, child: null }, child: null }

Conclusion

This approach effectively flattens the multilevel structure of the linked list by iterating through the nodes, identifying nodes with child lists, and integrating them into the main list. The necessary pointer manipulations ensure the correct connections between nodes, resulting in a flattened doubly linked list.

References