Framework Thinking - Neetcode 150 - Merge Two Sorted Linked Lists
Here is solutions for Merge Two Sorted Linked Lists.
1. Understand the Problem
- You are given two linked lists (list1 and list2) that are already sorted in non-decreasing order.
👉 The goal is to merge them into one sorted linked list, also in non-decreasing order. Return the head of the new merged list.
2. Clarify Constraints – Ask 4–5 Questions
- Can the lists be empty?
=> Yes, either list may be None.
- Are the linked lists guaranteed to be sorted?
=> Yes, both input lists are sorted in ascending order.
- Can we modify the existing nodes or must we create new nodes?
=> We can reuse the existing nodes (preferable to reduce memory).
- What is the expected time and space complexity?
=> Time: O(n + m), Space: O(1) (excluding output list).
- What if both lists consist of equal values (e.g., [1,1,1] and [1,1])?
=> Still merge preserving full list order.
3. Explore Examples
| Input List1 | Input List2 | Output |
|---|---|---|
| 1 → 2 → 4 | 1 → 3 → 4 | 1 → 1 → 2 → 3 → 4 → 4 |
| None | 0 → 2 | 0 → 2 |
| None | None | None |
| 5 → 10 | 1 → 3 → 4 | 1 → 3 → 4 → 5 → 10 |
| -3 → -1 | -2 → 2 | -3 → -2 → -1 → 2 |
4. Brainstorm 2-3 solutions
4.1. Naive solution, rebuild all the list - Time: O((n + m))*log(n + m), Space: O(n + m)
-
Build a new array size n + m.
-
Sort all of this => Build a new list.
-
Time: O((n + m))*log(n + m)
-
Space: O(n + m)
4.2. Optimize solution, merge sort - Time: O(n + m), Space: O(n + m)
-
Build a size n + m.
-
Apply two pointers to merge sort.
-
Time: O(n + m).
-
Space: O(n + m).
4.3. Dummy node - Time: O(n + m), Space: O(1)
-
Use dummy node to store to the address of the node in list1, list2, based on value.
-
Time: O(n + m).
-
Space: O(1).
5. Implement solutions
5.1. Recursive Solution - Time: O(N + M), Space: O(N + M), same with logic create new array
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
if list1 is None:
return list2
if list2 is None:
return list1
if list1.val <= list2.val:
list1.next = self.mergeTwoLists(list1.next, list2)
return list1
else:
list2.next = self.mergeTwoLists(list1, list2.next)
return list2
-
Time: O(N + M).
-
Space: O(N + M).
5.2. Dummy Node - Time: O(N + M), Space: O(1)
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeTwoLists(self, list1: ListNode, list2: ListNode) -> ListNode:
dummy = ListNode()
current = dummy
while list1 and list2:
if list1.val < list2.val:
current.next = list1
list1 = list1.next
else:
current.next = list2
list2 = list2.next
current = current.next
# Attach remaining nodes
if list1:
current.next = list1
else:
current.next = list2
return dummy.next
-
Time: O(N + M).
-
Space: O(1).
6. Dry run examples
list1 = [1 → 2 → 4] list2 = [1 → 3 → 4]
| Iteration | list1 | list2 | Attached to result | Result (so far) |
|---|---|---|---|---|
| Start | 1 → 2 → 4 | 1 → 3 → 4 | – | – |
| 1 | 1 → 2 → 4 | 3 → 4 | 1 | 1 |
| 2 | 2 → 4 | 3 → 4 | 1 | 1 → 1 |
| 3 | 4 | 3 → 4 | 2 | 1 → 1 → 2 |
| 4 | 4 | 4 | 3 | 1 → 1 → 2 → 3 |
| 5 | 4 | – | 4 | 1 → 1 → 2 → 3 → 4 |
| End | – | – | 4 (remaining) | 1 → 1 → 2 → 3 → 4 → 4 |