Solving Merge k Sorted Lists: Combining Multiple Sorted Lists
The "Merge k Sorted Lists" problem is about combining multiple sorted linked lists into one single sorted list.
Problem Statement
Given an array of k linked-lists lists, each linked-list is sorted in ascending order. Merge all the linked-lists into one sorted linked list and return it.
Example
Consider k sorted linked lists:
Example 1
- Input: lists =
[[1,4,5],[1,3,4],[2,6]] - Output:
[1,1,2,3,4,4,5,6] - Explanation: The linked-lists are:
[
1 -> 4 -> 5,
1 -> 3 -> 4,
2 -> 6
]
Merging them into one sorted list results in 1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6.
Example 2
- Input: lists =
[] - Output:
[] - Explanation: No lists to merge, so the output is an empty list.
Example 3
- Input: lists =
[[]] - Output:
[] - Explanation: A single empty list results in an empty merged list.
Solution Approach
A popular approach to solving this problem is using a Min Heap or a Priority Queue to efficiently find and merge the smallest elements of the lists.
Solution Approach - Min Heap
Breaking Down the Solution
The provided TypeScript solution for merging k sorted linked lists implements a custom Min Heap class and uses it to efficiently merge the lists. Here's a breakdown of how the solution works:
ListNode Class
- Represents a node in a linked list.
- Contains a value (
val) and a reference to the next node (next).
MinHeap Class
- A min heap is a binary tree where the parent node is always less than or equal to its children.
- The class maintains a heap in an array (
heap), where each element is aListNodeornull.
Key Methods and Properties:
getLeftChildIndex,getRightChildIndex,getParentIndex: Calculate the indices of a node's left child, right child, and parent.hasLeftChild,hasRightChild,hasParent: Check if the node at the given index has a left child, right child, or parent.leftChild,rightChild,parent: Get the left child, right child, or parent of the node at the given index.swap: Swap two nodes in the heap.isEmpty: Check if the heap is empty.insert: Add a new node to the heap and reorganize the heap to maintain the min heap property (heapify up).extract: Remove and return the smallest node from the heap and reorganize the heap (heapify down).
mergeKLists Function
- Merges
ksorted linked lists into one sorted linked list. - Uses the Min Heap to efficiently find the smallest current node among all the lists.
- Iteratively extracts the smallest node from the heap and adds it to the merged list.
- If the extracted node has a next node, inserts the next node into the heap.
Process:
- Initialize Min Heap: All head nodes of the
klists are inserted into the min heap. - Merging: Continuously extract the smallest node from the heap and attach it to the merged list.
- Insert Next Nodes: If the extracted node has a next node, insert that next node into the heap to be considered in subsequent extractions.
- Completion: The process continues until the heap is empty, meaning all nodes have been merged into the new list.
The result is a new linked list, pointed to by dummy.next, which is a sorted merge of all the input linked lists. This approach efficiently handles the merging process, making it suitable for a large number of lists or lists with a large number of nodes.
Conclusion
Merging k sorted lists is a classic problem that demonstrates the practical application of heap data structures in sorting and merging operations.
Rust Solution
Rust's BinaryHeap is a max-heap, so the Item wrapper implements Ord with the operands flipped — o.0.cmp(&self.0) — to invert the ordering and pop the smallest val first. Because next is an Option<Box<ListNode>>, advancing a list uses node.next.take(), which moves the tail out of the box and leaves None behind so ownership passes cleanly into the next heap.push. The while let Some(Item(_, mut node)) = heap.pop() binding destructures the tuple struct and takes ownership of the boxed node in a single step, avoiding any borrow of the heap's interior.
Go Solution
Go's container/heap isn't a ready-made heap but an algorithm over any type satisfying its interface, so PQ (a []Item slice) must supply Len, Less, Swap, Push, and Pop itself. Less returns p[i].val < p[j].val directly, giving a min-heap with no comparator inversion — the opposite of Rust's flipped Ord. The pre-generics interface hands elements around as interface{}, so Push recovers the concrete type with x.(Item) and Pop peels the last slice element via old[:n-1], with callers re-asserting through heap.Pop(pq).(Item).
Comments (0)
Stub — comments live in your browser only (localStorage). No server round-trip yet.
No comments yet. Be the first.