Create your own
Lesson illustration

Merging Sorted Sequences via Heap

Welcome to our second lesson on high-value interview patterns. In the previous lesson, we focused on finding a single element in an unsorted collection with Quickselect. Today, we'll tackle a different but equally common challenge: combining multiple, already-sorted collections into one.

This lesson addresses the "Merge K Sorted Lists" problem, a classic that appears frequently in interviews. You will learn how to leverage a min-heap (also known as a priority queue) to merge multiple sorted sequences far more efficiently than simpler, brute-force methods. This pattern is essential for problems involving streams of ordered data from multiple sources.

The Challenge: From Two Lists to K Lists

You've likely encountered the task of merging two sorted lists or arrays before, perhaps as part of implementing Merge Sort. The standard approach uses a "two-pointer" technique: you compare the elements at the current pointers of each list, append the smaller one to your result, and advance the corresponding pointer. This is a very efficient, linear-time operation.

Let's quickly review that foundational idea.

Merge K Sorted Arrays - Min Heap Algorithm ("Merge K Sorted Lists" on LeetCode)

The video "Merge K Sorted Arrays" from Back To Back SWE provides an excellent refresher on merging two sorted arrays before generalizing the concept.

Please watch the section from this clip, which demonstrates the two-pointer merge logic. Pay close attention to how we only ever need to compare the smallest available elements from each array.

This works beautifully for two lists. But what if we have k lists, where k could be dozens or even hundreds?

A naive extension would be to compare the current elements from all k lists in every single step, find the minimum, add it to the result, and advance the pointer for that list. To find the minimum of k items, you'd need k-1 comparisons. If there are N total elements across all lists, this approach would have a time complexity of roughly O(N * k). When k is large, this becomes very slow.

Another brute-force method is to simply concatenate all k lists into one large list and then sort it. With N total elements, this would take O(N log N) time. This approach completely ignores the fact that the input lists are already sorted. We can do better.

The Heap-Based Solution: A Smarter Way to Merge

The key insight is that, just like in the two-list case, we only ever care about the smallest available element from each list. We need a data structure that can efficiently manage these k "candidate" elements and tell us which one is the global minimum at any moment.

This is precisely the problem that a min-heap (or min-priority queue) is designed to solve.

23. Merge k Sorted Lists - In-Depth Explanation

The "Intuition" section of this AlgoMonster article clearly explains why a min-heap is the perfect tool for this problem.

Read the full section. It elegantly breaks down the problem from "merge k lists" to "repeatedly find the minimum of k elements."

As the article explains, by using a min-heap, we can find the smallest of the k current elements in O(1) time (it's always at the root) and update the heap after extracting it in O(log k) time. This is a huge improvement over the O(k) linear scan we discussed earlier.

The Algorithm Step-by-Step

The algorithm is a continuous cycle of extracting the minimum element and replenishing the heap with the next element from the same source list.

Here is the general process, which we'll visualize next:

  1. Initialization: Create a dummy head for the result list (a technique you've seen before to simplify head-of-list operations) and initialize a min-heap.
  2. Seeding the Heap: Insert the first node from each of the k lists into the min-heap. If a list is empty, you simply ignore it. The heap will now contain up to k nodes, ordered by their value.
  3. The Merge Loop: While the min-heap is not empty:
    a. Extract Minimum: Pop the node with the smallest value from the heap. This is the next node in our final sorted list.
    b. Append to Result: Append this node to the end of your result list.
    c. Replenish Heap: If the node you just extracted has a next node in its original list, push that next node into the heap. This maintains the invariant that the heap holds the smallest available candidate from each non-exhausted list.
  4. Finalize: The loop terminates when the heap is empty, which means all nodes from all lists have been processed. Return the next of your dummy head, which is the true head of the fully merged and sorted list.

Let's see this process in action. The following diagram illustrates the state of the lists, the priority queue (heap), and the merged result at each step.

This diagram shows four sorted linked lists being merged. A priority queue (min-heap) is used to keep track of the smallest node from each list. In each step, the node with the minimum value is popped from the queue, appended to the result list (starting with a dummy -1 node), and the next node from its original list is pushed into the queue. This continues until all lists are exhausted and the queue is empty, resulting in a single, fully sorted linked list.

To see the dynamics of this process, the following video provides a clear, animated walkthrough.

Merge K Sorted Linked Lists - Leetcode 23 - Heaps (Python)

This video from Greg Hogg provides a great visual explanation of the heap-based merge process.

Watch the segment from this part of the video. It traces how nodes are popped from the heap, added to the result, and how their successors are added back to the heap, maintaining the sorted order.

A TypeScript Implementation

Now, let's translate this logic into code. In JavaScript/TypeScript, a priority queue is not a built-in data structure, but it's readily available through popular libraries like @datastructures-js/priority-queue, which is often permitted in interview settings. The following implementation uses this library.

23. Merge k Sorted Lists - In-Depth Explanation

This section provides a clean, well-commented TypeScript implementation of the algorithm.

Find the TypeScript code block, which starts with the ListNode class definition. Read through the mergeKLists function, paying attention to these key parts: Initializing the MinPriorityQueue, specifying that nodes should be ordered by their val property. Seeding the heap with the head of each non-empty list. The main loop, where the algorithm extracts the min node, enqueues its successor, and appends it to the result list.

This implementation directly mirrors the algorithm we've discussed. It's a robust and efficient solution to the problem.

Complexity Analysis

Let's analyze the performance of this heap-based approach.

  • N: The total number of nodes across all lists.
  • k: The number of sorted lists.

23. Merge k Sorted Lists - In-Depth Explanation

This section of the AlgoMonster article gives a concise breakdown of the time and space complexity.

Read the full analysis.

As the resource explains:

  • Time Complexity:

    • We process each of the N nodes exactly once.
    • For each node, we perform one heap extraction (dequeue) and at most one heap insertion (enqueue).
    • The heap contains at most k elements (one from each list). Therefore, each heap operation costs .
    • The total time is the number of nodes multiplied by the cost per node, giving us . This is a significant improvement over the brute-force when k is small compared to N.
  • Space Complexity:

    • The primary auxiliary space is used by the min-heap.
    • At any given time, the heap stores at most k nodes.
    • Therefore, the auxiliary space complexity is . (The space for the final merged list is typically considered part of the output, not auxiliary space).

Conclusion

In this lesson, you've learned a powerful pattern for merging multiple sorted sequences. This technique, often called "k-way merge," is a staple in algorithm interviews and has practical applications in databases, distributed systems, and external sorting.

Here are the key takeaways:

  • The Problem: Merging k sorted lists efficiently. A naive comparison at each step (O(Nk)) or a full sort (O(N log N)) are suboptimal.
  • The Insight: At any step, we only need the smallest of the k current "head" elements from each list.
  • The Tool: A min-heap is the ideal data structure to track these k candidates, providing the global minimum in O(\log k) time per element.
  • The Algorithm: Seed a min-heap with the first element of each list. Repeatedly extract the minimum, add it to the result, and insert the next element from its source list into the heap.
  • The Performance: This approach yields an optimal time complexity of using auxiliary space.

In our next lesson, we will shift gears to a different type of problem and explore how to use a monotonic deque. This specialized queue structure is incredibly effective for finding minimum or maximum values over a sliding window of elements.

Can't find a good explanation? Sign up and we'll make it for you

Sign up