You are given an array of k linked lists lists, where each list is sorted in ascending order.
Return the sorted linked list that is the result of merging all of the individual linked lists.
Example 1:
Input: lists = [[1,2,4],[1,3,5],[3,6]]
Output: [1,1,2,3,3,4,5,6]Example 2:
Input: lists = []
Output: []Example 3:
Input: lists = [[]]
Output: []Constraints:
0 <= lists.length <= 10000 <= lists[i].length <= 100-1000 <= lists[i][j] <= 1000
You should aim for a solution as good or better than O(n * k) time and O(1) space, where k is the total number of lists and n is the total number of nodes across all lists.
A brute-force solution would involve storing all n nodes in an array, sorting them, and converting the array back into a linked list, resulting in an O(nlogn) time complexity. Can you think of a better way? Perhaps consider leveraging the idea behind merging two sorted linked lists.
We can merge two sorted linked lists without using any extra space. To handle k sorted linked lists, we can iteratively merge each linked list with a resultant merged list. How can you implement this?
We iterate through the list array with index i, starting at i = 1. We merge the linked lists using mergeTwoLists(lists[i], lists[i - 1]), which returns the head of the merged list. This head is stored in lists[i], and the process continues. Finally, the merged list is obtained at the last index, and we return its head.
The simplest way to merge all linked lists is to ignore the list structure, collect every value, sort them, and then rebuild a single sorted linked list.
This doesn’t use any clever merging logic — it is purely based on gathering and sorting.
It’s easy to implement but not efficient because sorting dominates the runtime.
nodes.nodes.nodes list.# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
nodes = []
for lst in lists:
while lst:
nodes.append(lst.val)
lst = lst.next
nodes.sort()
res = ListNode(0)
cur = res
for node in nodes:
cur.next = ListNode(node)
cur = cur.next
return res.nextWe repeatedly pick the smallest head node among all the lists and attach it to our result list.
At every step:
This is similar to merging k sorted arrays by always picking the smallest available element.
cur to the dummy.cur.next.cur forward.dummy.next.# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
res = ListNode(0)
cur = res
while True:
minNode = -1
for i in range(len(lists)):
if not lists[i]:
continue
if minNode == -1 or lists[minNode].val > lists[i].val:
minNode = i
if minNode == -1:
break
cur.next = lists[minNode]
lists[minNode] = lists[minNode].next
cur = cur.next
return res.nextWhere is the total number of lists and is the total number of nodes across lists.
Instead of merging all k lists at once, we can merge them one by one.
Each merge operation is just like the standard “merge two sorted linked lists” problem:
null (or equivalent).1 to k - 1:lists[i - 1] and lists[i] into a single sorted list.lists[i].lists[k - 1] will contain the fully merged list.lists[k - 1].Merging two lists (mergeList(l1, l2)):
tail pointer starting at the dummy.l1 and l2 are non-empty:l1.val and l2.val.tail.next.tail forward.tail.next.dummy.next as the merged head.# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if len(lists) == 0:
return None
for i in range(1, len(lists)):
lists[i] = self.mergeList(lists[i - 1], lists[i])
return lists[-1]
def mergeList(self, l1, l2):
dummy = ListNode()
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
if l1:
tail.next = l1
if l2:
tail.next = l2
return dummy.nextWhere is the total number of lists and is the total number of nodes across lists.
We want to always pick the smallest current node among all k lists as efficiently as possible.
Instead of scanning all heads every time (which is slow), we can use a min-heap (priority queue):
next node (if it exists) into the heap.This way, at every step we choose the globally smallest node in O(log k) time, where k is the number of lists.
cur pointing to it.cur.next, and move cur forward.next node, push that next node into the heap.dummy.next as the head of the merged list.# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class NodeWrapper:
def __init__(self, node):
self.node = node
def __lt__(self, other):
return self.node.val < other.node.val
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if len(lists) == 0:
return None
res = ListNode(0)
cur = res
minHeap = []
for lst in lists:
if lst is not None:
heapq.heappush(minHeap, NodeWrapper(lst))
while minHeap:
node_wrapper = heapq.heappop(minHeap)
cur.next = node_wrapper.node
cur = cur.next
if node_wrapper.node.next:
heapq.heappush(minHeap, NodeWrapper(node_wrapper.node.next))
return res.nextWhere is the total number of lists and is the total number of nodes across lists.
Instead of merging all k lists at once or one by one in order, we can use a divide and conquer strategy, similar to how merge sort works.
Idea:
By always merging pairs of lists, we reduce the total work compared to merging k lists sequentially.
Each merge of two lists is linear in their total length, and we do about log k levels of merging.
This makes the approach both clean and efficient.
Base Cases
lists is empty, return null.divide(lists, l, r):l > r, return null.l == r, return lists[l] (only one list to return).Divide Step
mid = (l + r) // 2.left = divide(lists, l, mid)right = divide(lists, mid + 1, r)Conquer Step (merge two lists)
left and right using the standard merge two sorted linked lists routine:curr pointer.curr.next.curr forward.Final Answer
divide(lists, 0, len(lists) - 1) and return the resulting list.This approach efficiently merges k sorted linked lists in a structured, recursive way.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists):
if not lists or len(lists) == 0:
return None
return self.divide(lists, 0, len(lists) - 1)
def divide(self, lists, l, r):
if l > r:
return None
if l == r:
return lists[l]
mid = l + (r - l) // 2
left = self.divide(lists, l, mid)
right = self.divide(lists, mid + 1, r)
return self.conquer(left, right)
def conquer(self, l1, l2):
dummy = ListNode(0)
curr = dummy
while l1 and l2:
if l1.val <= l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
if l1:
curr.next = l1
else:
curr.next = l2
return dummy.nextWhere is the total number of lists and is the total number of nodes across lists.
This is the same idea as divide and conquer, but done iteratively instead of using recursion.
We repeatedly merge the lists in pairs:
Each pairwise merge is just the usual merge of two sorted linked lists.
By always merging lists two at a time, the total work is efficient and structured, similar to the merge step in merge sort.
lists is empty, return null.mergedLists.lists in steps of 2:l1 = lists[i].l2 = lists[i + 1] if it exists, otherwise None.l1 and l2 using mergeList and append the result to mergedLists.lists = mergedLists.lists[0] is the fully merged sorted list. Return it.Merging two lists (mergeList(l1, l2)):
tail pointer pointing to it.l1 and l2 are non-empty:l1 and l2 to tail.next.tail forward.l1 or l2 to tail.next.dummy.next as the merged head.# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists or len(lists) == 0:
return None
while len(lists) > 1:
mergedLists = []
for i in range(0, len(lists), 2):
l1 = lists[i]
l2 = lists[i + 1] if (i + 1) < len(lists) else None
mergedLists.append(self.mergeList(l1, l2))
lists = mergedLists
return lists[0]
def mergeList(self, l1, l2):
dummy = ListNode()
tail = dummy
while l1 and l2:
if l1.val < l2.val:
tail.next = l1
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
if l1:
tail.next = l1
if l2:
tail.next = l2
return dummy.nextWhere is the total number of lists and is the total number of nodes across lists.