Merge Two Sorted Lists
Merge two sorted linked lists into one. Splicing should be done by moving existing nodes, not creating new ones.
- Nodes: 0 to 50 per list
- -100 <= Node.val <= 100
- Input lists are already sorted.
list1 = [1,2,4], list2 = [1,3,4][1,1,2,3,4,4]list1 = [], list2 = [0][0]list1 = [], list2 = [][]Merging two sorted lists is essentially about consolidating disparate, ordered data streams into a single, cohesive timeline. Since each list is already sorted, the globally next-smallest element must reside at the head of one of the two lists, allowing us to make local decisions that guarantee a globally sorted result.
The naive way to solve this is to collect all nodes from both lists into a new array, sort that array, and then create a completely new linked list from the sorted values. While this works, it requires O(N+M) extra space and ignores the fact that the inputs are already sorted. This is inefficient when we can simply re-route existing pointers.
# Brute force: collect, sort, and rebuild
def brute_force(l1, l2):
nodes = []
while l1: nodes.append(l1.val); l1 = l1.next
while l2: nodes.append(l2.val); l2 = l2.next
nodes.sort()
dummy = Node(0)
curr = dummy
for val in nodes:
curr.next = Node(val)
curr = curr.next
return dummy.nextBecause the lists are pre-sorted, we don't need to gather or re-sort anything. At any point, we simply look at the heads of both lists and pick the smaller one to be the next node in our new list. By maintaining a pointer on the head of each list and moving them forward as we pick nodes, we can construct the result list in a single pass.
We use a Dummy Node to act as a stable starting anchor for our result list, which simplifies our logic and avoids edge cases when initializing the head. We keep a tail pointer to append nodes to our new list.
- Compare the current nodes of l1 and l2.
- Append the smaller node to tail and move that list's pointer forward.
- Once one list is exhausted, we simply "leak" the remainder of the other list by pointing tail.next to the remaining nodes, as they are already in the correct sorted order.
# Optimal: iterative pointer re-routing
def merge_two_lists(l1, l2):
dummy = Node(0)
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
# Attach remaining part
tail.next = l1 or l2
return dummy.nextIterative 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).