23. Merge k Sorted List
Intuition
To merge k sorted linked lists efficiently, we can use a min heap (priority queue) to always get the smallest element among all lists. This approach allows us to maintain a sorted order while merging the lists.
Approach
- Create a min heap to store the head nodes of all k lists
- Initialize the heap with the first node from each list (if not nil)
- Create a dummy node to build the result list
- While the heap is not empty:
- Extract the minimum node from the heap
- Add it to the result list
- If the extracted node has a next node, push it back into the heap
- Return the merged list
Complexity
- Time complexity: O(N log k)
- Space complexity: O(k)
Keywords
- Linked List
- Heap/Priority Queue
- Merge Sort
Code
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
type Comparator func(child, parent interface{}) bool
type heap struct {
Storage []interface{}
CmpFunc Comparator
}
func NewHeap(cmpFunc Comparator) *heap {
return &heap {
Storage: append(make([]interface{}, 0), -1),
CmpFunc: cmpFunc,
}
}
func (h *heap) Len() int {
return len(h.Storage) - 1
}
func (h *heap) IsEmpty() bool {
return h.Len() == 0
}
func (h *heap) cmp(child, parent interface{}) bool {
return h.CmpFunc(child, parent)
}
func (h *heap) swap(x, y int) {
h.Storage[x], h.Storage[y] = h.Storage[y], h.Storage[x]
}
func (h *heap) Top() (interface{}, error) {
if h.IsEmpty() {
return nil, errors.New("Heap is empty.")
}
return h.Storage[1], nil
}
func (h *heap) Push(item interface{}) {
h.Storage = append(h.Storage, item)
now := h.Len()
for now / 2 > 0 && !h.cmp(h.Storage[now], h.Storage[now / 2]) {
h.swap(now, now / 2)
now /= 2
}
}
func (h *heap) Pop() (interface{}, error) {
top, err := h.Top()
if err != nil {
return nil, err
}
last := h.Len()
h.swap(1, last)
h.Storage = h.Storage[: last]
now := 1
for now < last {
left, right := 0, 0
if now * 2 < last && !h.cmp(h.Storage[now * 2], h.Storage[now]) {
left = now * 2
}
if now * 2 + 1 < last && !h.cmp(h.Storage[now * 2 + 1], h.Storage[now]) {
right = now * 2 + 1
}
if left == 0 && right == 0 {
break
} else if left != 0 && right == 0 {
h.swap(now, left)
now = left
} else if left == 0 && right != 0 {
h.swap(now, right)
now = right
} else {
if h.cmp(h.Storage[left], h.Storage[right]) {
h.swap(now, right)
now = right
} else {
h.swap(now, left)
now = left
}
}
}
return top, nil
}
func mergeKLists(lists []*ListNode) *ListNode {
if len(lists) == 0 {
return nil
}
cmpFunc := func(child, parent interface{}) bool {
return child.(*ListNode).Val > parent.(*ListNode).Val
}
hp := NewHeap(cmpFunc)
for _, node := range lists {
if node != nil {
hp.Push(node)
}
}
prev := &ListNode{Val: 0, Next: nil}
cur := prev
for !hp.IsEmpty() {
top, _ := hp.Pop()
cur.Next = top.(*ListNode)
cur = cur.Next
if next := top.(*ListNode).Next; next != nil {
hp.Push(next)
}
}
return prev.Next
}