LeetCode 21: Merge Two Sorted Lists
-
- [1. Problem Link 🔗](#1. Problem Link 🔗)
- [2. Solution Overview 🧭](#2. Solution Overview 🧭)
- [3. Solution 1: Iterative with Dummy Node (Recommended)](#3. Solution 1: Iterative with Dummy Node (Recommended))
-
- [3.1. Algorithm](#3.1. Algorithm)
- [3.2. Important Points](#3.2. Important Points)
- [3.3. Java Implementation](#3.3. Java Implementation)
- [3.4. Time & Space Complexity](#3.4. Time & Space Complexity)
- [4. Solution 2: Recursive Approach](#4. Solution 2: Recursive Approach)
-
- [4.1. Algorithm](#4.1. Algorithm)
- [4.2. Important Points](#4.2. Important Points)
- [4.3. Java Implementation](#4.3. Java Implementation)
- [4.4. Time & Space Complexity](#4.4. Time & Space Complexity)
- [5. Solution 3: Iterative without Dummy Node](#5. Solution 3: Iterative without Dummy Node)
-
- [5.1. Algorithm](#5.1. Algorithm)
- [5.2. Important Points](#5.2. Important Points)
- [5.3. Java Implementation](#5.3. Java Implementation)
- [5.4. Time & Space Complexity](#5.4. Time & Space Complexity)
- [6. Solution 4: In-place Merge (Modifying Original Lists)](#6. Solution 4: In-place Merge (Modifying Original Lists))
-
- [6.1. Algorithm](#6.1. Algorithm)
- [6.2. Important Points](#6.2. Important Points)
- [6.3. Java Implementation](#6.3. Java Implementation)
- [6.4. Time & Space Complexity](#6.4. Time & Space Complexity)
- [7. Solution 5: Recursive with Tail Optimization](#7. Solution 5: Recursive with Tail Optimization)
-
- [7.1. Algorithm](#7.1. Algorithm)
- [7.2. Important Points](#7.2. Important Points)
- [7.3. Java Implementation](#7.3. Java Implementation)
- [7.4. Time & Space Complexity](#7.4. Time & Space Complexity)
- [8. Solution Comparison 📊](#8. Solution Comparison 📊)
- [9. Summary 📝](#9. Summary 📝)
1. Problem Link 🔗
LeetCode 21: Merge Two Sorted Lists
2. Solution Overview 🧭
Merge two sorted linked lists and return it as a sorted list. The list should be made by splicing together the nodes of the first two lists.
Example:
Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]
Constraints:
- The number of nodes in both lists is in the range
[0, 50] -100 <= Node.val <= 100- Both lists are sorted in non-decreasing order
Common approaches include:
- Iterative Approach: Use a dummy node and build the merged list step by step
- Recursive Approach: Recursively merge the lists by comparing heads
- In-place Merge: Merge without using extra space by rearranging pointers
3. Solution 1: Iterative with Dummy Node (Recommended)
3.1. Algorithm
- Create a dummy node to simplify edge cases
- Use a current pointer to build the new list
- Compare nodes from both lists and attach the smaller one
- Handle remaining nodes after one list is exhausted
3.2. Important Points
- Most intuitive and commonly used approach
- Handles edge cases gracefully
- Easy to understand and implement
3.3. Java Implementation
java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// Create a dummy node to simplify code
ListNode dummy = new ListNode(-1);
ListNode current = dummy;
// Traverse both lists
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}
// Attach remaining nodes from either list
if (list1 != null) {
current.next = list1;
} else {
current.next = list2;
}
return dummy.next;
}
}
3.4. Time & Space Complexity
- Time Complexity: O(m + n) where m and n are lengths of the two lists
- Space Complexity: O(1) - only using a constant amount of extra space
4. Solution 2: Recursive Approach
4.1. Algorithm
- Compare the heads of both lists
- Set the smaller head as the current node
- Recursively merge the remaining lists
- Base case: when one list is null, return the other list
4.2. Important Points
- Elegant and concise
- Uses O(m + n) stack space for recursion
- Good for understanding recursive thinking
4.3. Java Implementation
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// Base cases
if (list1 == null) return list2;
if (list2 == null) return list1;
// Compare and recursively merge
if (list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
}
4.4. Time & Space Complexity
- Time Complexity: O(m + n)
- Space Complexity: O(m + n) - recursion stack
5. Solution 3: Iterative without Dummy Node
5.1. Algorithm
- Handle the head initialization separately
- Use a current pointer to build the list
- Similar to dummy node approach but handles head case explicitly
5.2. Important Points
- Saves one node of memory
- More complex edge case handling
- Good for understanding pointer manipulation
5.3. Java Implementation
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
ListNode head, current;
// Initialize head
if (list1.val <= list2.val) {
head = list1;
list1 = list1.next;
} else {
head = list2;
list2 = list2.next;
}
current = head;
// Merge the lists
while (list1 != null && list2 != null) {
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 != null) {
current.next = list1;
} else {
current.next = list2;
}
return head;
}
}
5.4. Time & Space Complexity
- Time Complexity: O(m + n)
- Space Complexity: O(1)
6. Solution 4: In-place Merge (Modifying Original Lists)
6.1. Algorithm
- Merge the lists by rearranging pointers without dummy node
- Choose the smaller head as the main list
- Insert nodes from the other list into the main list
6.2. Important Points
- More complex pointer manipulation
- Modifies the original lists
- Memory efficient
6.3. Java Implementation
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
// Ensure list1 starts with the smaller value
if (list1.val > list2.val) {
ListNode temp = list1;
list1 = list2;
list2 = temp;
}
ListNode head = list1;
while (list1 != null && list2 != null) {
ListNode prev = null;
// Traverse list1 until we find where to insert from list2
while (list1 != null && list1.val <= list2.val) {
prev = list1;
list1 = list1.next;
}
// Insert list2 node
prev.next = list2;
// Swap list1 and list2 to continue
if (list1 != null) {
ListNode temp = list1;
list1 = list2;
list2 = temp;
}
}
return head;
}
}
6.4. Time & Space Complexity
- Time Complexity: O(m + n)
- Space Complexity: O(1)
7. Solution 5: Recursive with Tail Optimization
7.1. Algorithm
- Optimized recursive approach
- Avoids some stack frames by handling consecutive nodes iteratively
- Combines recursion with iterative optimization
7.2. Important Points
- More efficient recursion
- Reduces stack depth in some cases
- Still uses O(min(m, n)) stack space
7.3. Java Implementation
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// Base cases
if (list1 == null) return list2;
if (list2 == null) return list1;
ListNode head;
if (list1.val <= list2.val) {
head = list1;
// Optimize by handling consecutive smaller nodes iteratively
while (list1.next != null && list1.next.val <= list2.val) {
list1 = list1.next;
}
head.next = mergeTwoLists(list1.next, list2);
} else {
head = list2;
// Optimize by handling consecutive smaller nodes iteratively
while (list2.next != null && list2.next.val <= list1.val) {
list2 = list2.next;
}
head.next = mergeTwoLists(list1, list2.next);
}
return head;
}
}
7.4. Time & Space Complexity
- Time Complexity: O(m + n)
- Space Complexity: O(min(m, n)) - optimized recursion stack
8. Solution Comparison 📊
| Solution | Time Complexity | Space Complexity | Advantages | Disadvantages |
|---|---|---|---|---|
| Iterative with Dummy | O(m+n) | O(1) | Simple, handles edges well | Uses one extra node |
| Recursive | O(m+n) | O(m+n) | Elegant, concise | Stack overflow risk |
| Iterative without Dummy | O(m+n) | O(1) | No extra node | Complex edge handling |
| In-place Merge | O(m+n) | O(1) | Memory efficient | Complex pointer logic |
| Optimized Recursive | O(m+n) | O(min(m,n)) | Better recursion | Still uses stack space |
9. Summary 📝
- Key Insight: Compare nodes from both lists and always choose the smaller one to maintain sorted order
- Recommended Approach: Solution 1 (Iterative with Dummy Node) is most commonly used in interviews
- Pattern Recognition: This is a fundamental merge pattern used in many algorithms like merge sort
- Edge Cases: Handle empty lists and lists of different lengths gracefully
Why Dummy Node is Helpful:
- Eliminates special case for the head node
- Simplifies code logic
- Makes the algorithm more readable and maintainable
The iterative approach with dummy node is generally preferred for its simplicity, efficiency, and readability.