Flatten a Multilevel Doubly Linked List
Initialize traversal at head node 1
Start with the head node (value 1). The current pointer is set to node 1, and the stack shows the traversal starting point.
curr = headMove to node 2 (no child)
Move the current pointer from node 1 to node 2. Node 2 has no child, so no splicing occurs.
curr = curr.nextMove to node 3 (child detected)
Current pointer moves to node 3, which has a child pointer to node 7. The algorithm prepares to flatten this child list.
if curr.child:Find tail of child list starting at node 7
Traverse the child list starting at node 7 to find its tail node (node 10). This tail will be connected to node 4.
tail = child
while tail.next:
tail = tail.nextConnect tail node 10 to node 4
Connect the tail of the child list (node 10) to node 4, preserving the original next nodes after node 3.
if curr.next:
tail.next = curr.next
curr.next.prev = tailSplice child list starting at node 7 after node 3
Set node 3's next pointer to its child (node 7) and update child's prev pointer to node 3, effectively splicing the child list in.
curr.next = child
child.prev = curr
curr.child = NoneMove to node 7 (child of node 3)
Traverse into the newly spliced child list starting at node 7, which has no child pointer itself.
curr = curr.nextMove to node 8 (child detected)
At node 8, detect a child pointer to node 11, triggering flattening of this nested child list.
if curr.child:Find tail of nested child list starting at node 11
Traverse the nested child list starting at node 11 to find its tail node (node 12).
tail = child
while tail.next:
tail = tail.nextConnect tail node 12 to node 9
Connect the tail of the nested child list (node 12) to node 9, preserving the original next nodes after node 8.
if curr.next:
tail.next = curr.next
curr.next.prev = tailSplice nested child list starting at node 11 after node 8
Set node 8's next pointer to its child (node 11) and update child's prev pointer to node 8, splicing the nested child list in.
curr.next = child
child.prev = curr
curr.child = NoneTraverse nodes 11 and 12 (no children)
Move through nodes 11 and 12 sequentially; neither has children, so traversal continues linearly.
curr = curr.nextMove to node 12
Advance current pointer to node 12, continuing traversal through the flattened nested child list.
curr = curr.nextMove to node 9 (continuing after nested child list)
After finishing nested child list, move to node 9, which has no child pointer.
curr = curr.nextMove to node 10 (no child)
Traverse to node 10, the tail of the first child list, which has no child pointer.
curr = curr.nextMove to node 4 (continue main list after child)
After finishing the child list, move to node 4, continuing traversal on the main list.
curr = curr.nextTraverse nodes 5 and 6 (no children)
Traverse nodes 5 and 6 sequentially; neither has children, so traversal continues to the end.
curr = curr.nextMove to node 6 (end of list)
Move to node 6, the last node in the list, which has no child pointer.
curr = curr.nextTraversal complete, return flattened list head
Current pointer is now null, indicating traversal and flattening are complete. Return the head node as the flattened list.
return headclass Node:
def __init__(self, val, prev=None, next=None, child=None):
self.val = val
self.prev = prev
self.next = next
self.child = child
def flatten(head):
curr = head # STEP 1: Initialize traversal
while curr:
if curr.child: # STEP 3,8: Child detected
child = curr.child
tail = child
while tail.next: # STEP 4,9: Find tail of child list
tail = tail.next
if curr.next: # STEP 5,10: Connect tail to curr.next
tail.next = curr.next
curr.next.prev = tail
curr.next = child # STEP 6,11: Splice child list
child.prev = curr
curr.child = None
curr = curr.next # STEP 2,7,12-18: Move to next node
return head # STEP 19: Return flattened list headKey Takeaways
This insight is hard to see from code alone because pointer updates are subtle and interdependent.
Visualizing the tail traversal clarifies why this step is necessary to avoid losing nodes.
Seeing the child pointer cleared in the trace shows how the algorithm avoids infinite loops.
