Merge Two Sorted Lists
Given the heads of two linked lists that are each already sorted in non-decreasing order, splice them into one sorted linked list and return its head. Build the result by relinking the existing nodes (do not allocate new ones). Either list may be empty; if both are empty, return null (an empty list).
- 0 to 50 nodes per list
- -100 <= Node.val <= 100
- Both input lists are already sorted in non-decreasing order
list1 = [1,2,4], list2 = [1,3,4][1,1,2,3,4,4]list1 = [], list2 = [0][0]list1 = [], list2 = [][]We are handed two lists that are already sorted, and we want one sorted list out. The fact that they are pre-sorted is the whole gift — it means we never have to sort anything.
You could pour every value into an array, sort it, and build a fresh list. That is O((N+M) log(N+M)) time for a sort we did not need, plus O(N+M) space for a brand-new list — when the answer is just the same nodes in a new order.
vals = []
while l1: vals.append(l1.val); l1 = l1.next
while l2: vals.append(l2.val); l2 = l2.next
vals.sort()
dummy = tail = Node(0)
for v in vals:
tail.next = Node(v); tail = tail.next
return dummy.nextHere is a new twist on two pointers: instead of two markers walking the same array, we put one marker on each list and walk them forward independently. (You saw a first taste of this in Is Subsequence, where two pointers crawled two different strings.) The key fact: because both lists are sorted, the smallest value not yet placed is always sitting at the head of one list or the other — it can't be hiding deeper in, because everything behind a head is larger. So we just compare the two current heads, take the smaller one, and advance that list's pointer. Repeat, and the output comes out sorted.
dummy = tail = Node(0) # dummy avoids special-casing the first node
while l1 and l2:
if l1.val <= l2.val:
tail.next = l1 # smaller head joins the result
l1 = l1.next
else:
tail.next = l2
l2 = l2.next
tail = tail.next
tail.next = l1 or l2 # one list is empty; attach whatever remains
return dummy.nextTwo small but important pieces. The dummy node is a throwaway node we hang the result off of, so we never need an if to handle "is this the very first node?" — we just return dummy.next at the end. And the last line, tail.next = l1 or l2: once one list runs dry, the other's remaining nodes are already sorted and already larger than everything placed, so we splice the whole tail on in one move instead of comparing node by node.
<= (not <) keeps the merge stable — when the two heads tie, taking l1 first preserves the original relative order. It doesn't change correctness here, but the habit matters in sorts where equal keys carry other data.This is the third shape of the two-pointer pattern. The same-direction form walked one array with a fast and slow pointer; the converging form walked one array from both ends; this multiple-sequence form runs a pointer through each of two (or more) separate sequences at once. The reusable skeleton has three slots: (a) compare the current fronts of the sequences, (b) decide which sequence to take from and advance, (c) decide what to emit. Fill them differently and you get different problems — here (a) compare heads, (b) advance the smaller, (c) emit that node. It only works because each sequence is sorted, so its smallest remaining item is always at its front. This exact merge is the "combine" step of merge sort, and scaling it from two lists to many is the next problem, Merge k Sorted Lists. Recognition signal: whenever you must combine or compare two already-sorted sequences in order, reach for one pointer per sequence and advance whichever front loses the comparison.
Iterative Two-Pointer Sync
Current Focus
Phase 1: Dual Sentinel Entry
We use a Dummy Node as our anchor. Pointers L1 and L2 stand at the gates of our two sorted streams.
We reuse existing node pointers. No new nodes are created (except the dummy), making this an **O(1) extra space** operation.
By comparing nodes one by one and appending the smaller to the new tail, we build the final list in a single pass of O(N + M).