Merge K Sorted Lists - JS Solution

JS
Editorial

Solution explanation

Solution explanation

Approach

We are given multiple sorted linked lists and we want to merge them into one fully sorted linked list. The main challenge is to always know which node among all lists should come next in sorted order.

The key idea is to always pull the globally smallest available node, append it to the merged list, and then advance only the list that node came from. We do this efficiently using a MinHeap.

We also maintain a temporary dummy node at the start of the result list. The dummy node is just a placeholder node whose .next will point to the true head of the merged list. We keep a current pointer that always points to the last node we've attached so far. At the end, we simply return dummy.next as the head of the merged list (and we ignore the dummy itself).

High-Level Strategy

  1. Step 1: Initialize the MinHeap with the head of each list.
    We create a MinHeap whose priority is based on node.val. Then, for each non-empty list, we push that list’s head node into the heap.

    Because each input list is already sorted, its head node is the smallest node that list can currently offer. That means at this moment, the heap contains exactly the smallest (first unprocessed) node from each list.

    The MinHeap is therefore storing all current “candidates,” and the node at the top of the heap is the globally smallest remaining node across all lists.
  2. Step 2: Build the merged list by repeatedly taking the smallest node.
    We maintain a dummy node and a current pointer, which walks along the output list we are building.

    While the heap is not empty:
    • Dequeue the front of the MinHeap as smallestNode. This node is the smallest among all nodes that are still unmerged.
    • Append smallestNode to the merged list by doing current.next = smallestNode, then advance the current pointer by doing current = current.next.
    • If smallestNode had a .next, we push that .next node into the MinHeap so it can compete for the next merge position.
    Why only .next?
    Because once we pull a node from some list, the only remaining candidate from that list that could possibly compete to be the next smallest is exactly that node’s .next — the lists are individually sorted, so nothing earlier in that list remains unprocessed, and nothing later in that list can be smaller than .next.
  3. Repeat until the heap is empty.
    Each loop extracts the globally smallest node and attaches it in order. When the heap is finally empty, it means we have consumed every node from every list and connected them (in ascending order) into a single merged chain starting at dummy.next.

Why this Works

  • The heap always holds the best next choice:
    At any point, the heap contains at most one “active” node per list: the first not-yet-merged node of that list. Taking the minimum from that set guarantees we always choose the globally smallest available value next.
  • We advance lists lazily:
    When we take a node from a given list, we don’t immediately dump the rest of that list into the heap. We only push node.next. That keeps the heap as small as possible (at most one node per list at a time, plus any nodes that became .next after merges).
  • We attach original nodes directly (no cloning):
    Instead of creating new nodes and copying values, we reuse the actual nodes from the input lists. Each time we pop the smallest node from the heap, we attach that exact node after current in the merged list.

    While we are building the merged list, the node we just attached may still have its original .next pointer into the remainder of its source list. That’s fine: as the algorithm continues, those remaining nodes will be placed in their correct positions, and by the end, the tail of the merged list will naturally point to null.
  • The dummy node simplifies head handling:
    Using a dummy node means we don't have to treat "attach the very first node" as a special case. We always attach to current.next, update current, and keep going. At the end we just return dummy.next, which is the real head of the merged list.

Time Complexity

Let k be the number of input lists, and let N be the total number of nodes across all lists combined.

We insert each node into the MinHeap once and remove it once. Each heap operation (enqueue/dequeue) costs O(log k), since the heap holds at most one candidate per list at a time. Doing this for all N nodes across all lists gives O(N log k).

Space Complexity

Let k be the number of input lists, and let N be the total number of nodes across all lists combined.

Even though the final merged list has N nodes linked together, those nodes (and their existing .next pointers) come directly from the input lists. We only rewired those pointers — we did not allocate any new nodes or new pointer fields for them.

The only additional data structure we maintain is the MinHeap, which can hold up to k node references at once. That’s O(k) extra space, plus a few temporary pointers like dummy and current (O(1)).

Therefore, the overall auxiliary space usage of the algorithm is O(k).

Visucodize editorial solution titled JS Solution for LeetCode's Merge K Sorted Lists coding problem. Includes code and may include explanation. You can animate the code step by step using the provided input by clicking the run button, or fork it locally to update the code and input for custom visualization. View the original problem at https://leetcode.com/problems/merge-k-sorted-lists.