Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.thealgorithms.datastructures.lists;
/**
* Implements an algorithm to flatten a multilevel linked list.
*
* In this specific problem structure, each node has a `next` pointer (to the
* next node at the same level) and a `child` pointer (which points to the head
* of another sorted linked list). The goal is to merge all these lists into a
* single, vertically sorted linked list using the `child` pointer.
*
* The approach is a recursive one that leverages a merge utility, similar to
* the merge step in Merge Sort. It recursively flattens the list starting from
* the rightmost node and merges each node's child list with the already
* flattened list to its right.
* @see <a href="https://www.geeksforgeeks.org/flattening-a-linked-list/">GeeksforGeeks: Flattening a Linked List</a>
*/
public final class FlattenMultilevelLinkedList {
/**
* Private constructor to prevent instantiation of this utility class.
*/
private FlattenMultilevelLinkedList() {
}
/**
* Node represents an element in the multilevel linked list. It contains the
* integer data, a reference to the next node at the same level, and a
* reference to the head of a child list.
*/
static class Node {
int data;
Node next;
Node child;

Node(int data) {
this.data = data;
this.next = null;
this.child = null;
}
}

/**
* Merges two sorted linked lists (connected via the `child` pointer).
* This is a helper function for the main flatten algorithm.
*
* @param a The head of the first sorted list.
* @param b The head of the second sorted list.
* @return The head of the merged sorted list.
*/
private static Node merge(Node a, Node b) {
// If one of the lists is empty, return the other.
if (a == null) {
return b;
}
if (b == null) {
return a;
}

Node result;

// Choose the smaller value as the new head.
if (a.data < b.data) {
result = a;
result.child = merge(a.child, b);
} else {
result = b;
result.child = merge(a, b.child);
}
result.next = null; // Ensure the merged list has no `next` pointers.
return result;
}

/**
* Flattens a multilevel linked list into a single sorted list.
* The flattened list is connected using the `child` pointers.
*
* @param head The head of the top-level list (connected via `next` pointers).
* @return The head of the fully flattened and sorted list.
*/
public static Node flatten(Node head) {
// Base case: if the list is empty or has only one node, it's already flattened.
if (head == null || head.next == null) {
return head;
}

// Recursively flatten the list starting from the next node.
head.next = flatten(head.next);

// Now, merge the current list (head's child list) with the flattened rest of the list.
head = merge(head, head.next);

// Return the head of the fully merged list.
return head;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.thealgorithms.datastructures.lists;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
* Unit tests for the FlattenMultilevelLinkedList class.
* This class tests the flattening logic with various list structures,
* including null lists, simple lists, and complex multilevel lists.
*/
final class FlattenMultilevelLinkedListTest {

// A helper function to convert a flattened list (connected by child pointers)
// into a standard Java List for easy comparison.
private List<Integer> toList(FlattenMultilevelLinkedList.Node head) {
List<Integer> list = new ArrayList<>();
FlattenMultilevelLinkedList.Node current = head;
while (current != null) {
list.add(current.data);
current = current.child;
}
return list;
}

@Test
@DisplayName("Test with a null list")
void testFlattenNullList() {
assertNull(FlattenMultilevelLinkedList.flatten(null));
}

@Test
@DisplayName("Test with a simple, single-level list")
void testFlattenSingleLevelList() {
// Create a simple list: 1 -> 2 -> 3
FlattenMultilevelLinkedList.Node head = new FlattenMultilevelLinkedList.Node(1);
head.next = new FlattenMultilevelLinkedList.Node(2);
head.next.next = new FlattenMultilevelLinkedList.Node(3);

// Flatten the list
FlattenMultilevelLinkedList.Node flattenedHead = FlattenMultilevelLinkedList.flatten(head);

// Expected output: 1 -> 2 -> 3 (vertically)
List<Integer> expected = List.of(1, 2, 3);
assertEquals(expected, toList(flattenedHead));
}

@Test
@DisplayName("Test with a complex multilevel list")
void testFlattenComplexMultilevelList() {
// Create the multilevel structure from the problem description
// 5 -> 10 -> 19 -> 28
// | | | |
// 7 20 22 35
// | | |
// 8 50 40
// | |
// 30 45
FlattenMultilevelLinkedList.Node head = new FlattenMultilevelLinkedList.Node(5);
head.child = new FlattenMultilevelLinkedList.Node(7);
head.child.child = new FlattenMultilevelLinkedList.Node(8);
head.child.child.child = new FlattenMultilevelLinkedList.Node(30);

head.next = new FlattenMultilevelLinkedList.Node(10);
head.next.child = new FlattenMultilevelLinkedList.Node(20);

head.next.next = new FlattenMultilevelLinkedList.Node(19);
head.next.next.child = new FlattenMultilevelLinkedList.Node(22);
head.next.next.child.child = new FlattenMultilevelLinkedList.Node(50);

head.next.next.next = new FlattenMultilevelLinkedList.Node(28);
head.next.next.next.child = new FlattenMultilevelLinkedList.Node(35);
head.next.next.next.child.child = new FlattenMultilevelLinkedList.Node(40);
head.next.next.next.child.child.child = new FlattenMultilevelLinkedList.Node(45);

// Flatten the list
FlattenMultilevelLinkedList.Node flattenedHead = FlattenMultilevelLinkedList.flatten(head);

// Expected sorted output
List<Integer> expected = List.of(5, 7, 8, 10, 19, 20, 22, 28, 30, 35, 40, 45, 50);
assertEquals(expected, toList(flattenedHead));
}

@Test
@DisplayName("Test with some empty child lists")
void testFlattenWithEmptyChildLists() {
// Create a list: 5 -> 10 -> 12
// | |
// 7 11
// |
// 9
FlattenMultilevelLinkedList.Node head = new FlattenMultilevelLinkedList.Node(5);
head.child = new FlattenMultilevelLinkedList.Node(7);
head.child.child = new FlattenMultilevelLinkedList.Node(9);

head.next = new FlattenMultilevelLinkedList.Node(10); // No child list
head.next.child = null;

head.next.next = new FlattenMultilevelLinkedList.Node(12);
head.next.next.child = new FlattenMultilevelLinkedList.Node(16);

// Flatten the list
FlattenMultilevelLinkedList.Node flattenedHead = FlattenMultilevelLinkedList.flatten(head);

// Expected sorted output
List<Integer> expected = List.of(5, 7, 9, 10, 12, 16);
assertEquals(expected, toList(flattenedHead));
}
}