Skip to content

Commit 2927d50

Browse files
Leetcode 21
1 parent ac35827 commit 2927d50

1 file changed

Lines changed: 187 additions & 98 deletions

File tree

Leetcode/Leetcode_21.py

Lines changed: 187 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,237 @@
11
"""
2-
LeetCode 21. Merge Two Sorted Lists
2+
=========================================================
3+
21. Merge Two Sorted Lists
4+
LeetCode: https://leetcode.com/problems/merge-two-sorted-lists/
35
4-
Problem:
5-
You are given the heads of two sorted linked lists, list1 and list2.
6+
Approach 1: Brute Force (Create New List)
7+
Approach 2: Optimal (Iterative In-Place)
68
7-
Merge the two lists into one sorted linked list and return the head
8-
of the merged list.
9+
Author: Your Name
10+
=========================================================
11+
12+
Problem Statement
13+
-----------------
14+
You are given the heads of two sorted linked lists.
15+
16+
Merge the two lists into one sorted linked list by splicing together
17+
the nodes of the first two lists.
18+
19+
Return the head of the merged linked list.
920
1021
Example:
1122
Input:
12-
list1 = 1 -> 2 -> 4
13-
list2 = 1 -> 3 -> 4
23+
List1 = [1,2,4]
24+
List2 = [1,3,4]
1425
1526
Output:
16-
1 -> 1 -> 2 -> 3 -> 4 -> 4
27+
[1,1,2,3,4,4]
28+
"""
29+
30+
31+
# =========================================================
32+
# Definition for singly-linked list.
33+
# =========================================================
34+
# class ListNode:
35+
# def __init__(self, val=0, next=None):
36+
# self.val = val
37+
# self.next = next
1738

18-
---------------------------------------------------------
19-
Approach: Recursive
20-
---------------------------------------------------------
2139

22-
Idea:
23-
Since both linked lists are already sorted, compare the current
24-
nodes of both lists.
40+
# =========================================================
41+
# APPROACH 1 : BRUTE FORCE (Create a New Linked List)
42+
# =========================================================
43+
"""
44+
Algorithm
45+
---------
46+
1. Create a dummy node.
47+
2. Traverse both linked lists simultaneously.
48+
3. Compare current nodes.
49+
4. Create a new node with the smaller value and attach it.
50+
5. Move the corresponding pointer.
51+
6. After one list ends, copy the remaining nodes of the other list.
52+
7. Return dummy.next.
53+
54+
Time Complexity:
55+
O(m + n)
56+
57+
Space Complexity:
58+
O(m + n)
59+
(New nodes are created.)
60+
"""
2561

26-
1. If list1's value is smaller (or equal), choose list1's node.
27-
2. Recursively merge the remaining nodes of list1 with list2.
28-
3. If list2's value is smaller, choose list2's node.
29-
4. Recursively merge list1 with the remaining nodes of list2.
30-
5. Continue until one list becomes empty.
3162

32-
Base Cases:
33-
- If list1 is empty, return list2.
34-
- If list2 is empty, return list1.
63+
class Solution:
64+
def mergeTwoLists(self, head1: Optional[ListNode], head2: Optional[ListNode]) -> Optional[ListNode]:
3565

36-
Why it works:
37-
At every recursive call, the smallest available node is placed
38-
into the merged list, maintaining sorted order.
66+
dummy = ListNode(0)
67+
tail = dummy
3968

40-
---------------------------------------------------------
41-
Dry Run
42-
---------------------------------------------------------
69+
while head1 and head2:
4370

44-
list1 = 1 -> 2 -> 4
45-
list2 = 1 -> 3 -> 4
71+
if head1.val <= head2.val:
72+
tail.next = ListNode(head1.val)
73+
head1 = head1.next
74+
else:
75+
tail.next = ListNode(head2.val)
76+
head2 = head2.next
4677

47-
Compare:
48-
1 <= 1
78+
tail = tail.next
4979

50-
Choose first 1
80+
while head1:
81+
tail.next = ListNode(head1.val)
82+
head1 = head1.next
83+
tail = tail.next
5184

52-
1 -> merge(2->4, 1->3->4)
85+
while head2:
86+
tail.next = ListNode(head2.val)
87+
head2 = head2.next
88+
tail = tail.next
5389

54-
Compare:
55-
2 > 1
90+
return dummy.next
5691

57-
Choose second 1
5892

59-
1 -> merge(2->4, 3->4)
93+
# =========================================================
94+
# APPROACH 2 : OPTIMAL (Iterative In-Place)
95+
# =========================================================
96+
"""
97+
Algorithm
98+
---------
99+
1. Create a dummy node.
100+
2. Maintain a tail pointer.
101+
3. Compare the current nodes of both lists.
102+
4. Attach the smaller node directly to tail.
103+
5. Move the corresponding list pointer.
104+
6. Move tail forward.
105+
7. Once one list becomes empty, attach the remaining list.
106+
8. Return dummy.next.
60107
61-
Compare:
62-
2 <= 3
108+
Example
109+
-------
110+
List1 : 1 -> 2 -> 4
111+
List2 : 1 -> 3 -> 4
63112
64-
Choose 2
113+
dummy
65114
66-
2 -> merge(4, 3->4)
115+
Step 1:
116+
dummy -> 1
117+
tail -> 1
67118
68-
Compare:
69-
4 > 3
119+
Step 2:
120+
dummy -> 1 -> 1
70121
71-
Choose 3
122+
Step 3:
123+
dummy -> 1 -> 1 -> 2
72124
73-
3 -> merge(4, 4)
125+
Step 4:
126+
dummy -> 1 -> 1 -> 2 -> 3
74127
75-
Compare:
76-
4 <= 4
128+
Step 5:
129+
dummy -> 1 -> 1 -> 2 -> 3 -> 4
77130
78-
Choose first 4
131+
Attach remaining node:
79132
80-
4 -> merge(None, 4)
133+
dummy -> 1 -> 1 -> 2 -> 3 -> 4 -> 4
81134
82-
Return remaining list:
135+
Return dummy.next.
136+
"""
83137

84-
4
85138

86-
Final Result:
139+
class Solution:
140+
def mergeTwoLists(self, head1: Optional[ListNode], head2: Optional[ListNode]) -> Optional[ListNode]:
87141

88-
1 -> 1 -> 2 -> 3 -> 4 -> 4
142+
dummy = ListNode(0)
143+
tail = dummy
89144

90-
---------------------------------------------------------
91-
Time Complexity: O(m + n)
92-
---------------------------------------------------------
93-
m = length of list1
94-
n = length of list2
145+
while head1 and head2:
95146

96-
Each node is visited exactly once.
147+
if head1.val <= head2.val:
148+
tail.next = head1
149+
head1 = head1.next
150+
else:
151+
tail.next = head2
152+
head2 = head2.next
97153

98-
---------------------------------------------------------
99-
Space Complexity: O(m + n)
100-
---------------------------------------------------------
101-
Recursive call stack can grow up to m + n calls.
154+
tail = tail.next
102155

103-
---------------------------------------------------------
156+
# Attach remaining nodes
157+
if head1:
158+
tail.next = head1
159+
else:
160+
tail.next = head2
161+
162+
return dummy.next
163+
164+
165+
# =========================================================
166+
# APPROACH 3 : Recursive
167+
# =========================================================
168+
"""
169+
Algorithm
170+
---------
171+
1. If one list is empty, return the other list.
172+
2. Compare the first nodes.
173+
3. Recursively merge the remaining lists.
174+
4. Return the smaller node as the head.
175+
176+
Time Complexity:
177+
O(m + n)
178+
179+
Space Complexity:
180+
O(m + n)
181+
(Recursive call stack)
104182
"""
105183

106-
# Definition for singly-linked list.
107-
# class ListNode:
108-
# def __init__(self, val=0, next=None):
109-
# self.val = val
110-
# self.next = next
111184

112185
class Solution:
113-
def mergeTwoLists(
114-
self,
115-
list1: Optional[ListNode],
116-
list2: Optional[ListNode]
117-
) -> Optional[ListNode]:
186+
def mergeTwoLists(self, head1: Optional[ListNode], head2: Optional[ListNode]) -> Optional[ListNode]:
118187

119-
# Base Case 1:
120-
# If list1 is empty, return list2
121-
if list1 is None:
122-
return list2
188+
if head1 is None:
189+
return head2
123190

124-
# Base Case 2:
125-
# If list2 is empty, return list1
126-
if list2 is None:
127-
return list1
191+
if head2 is None:
192+
return head1
128193

129-
# Choose the smaller node
130-
if list1.val <= list2.val:
194+
if head1.val <= head2.val:
195+
head1.next = self.mergeTwoLists(head1.next, head2)
196+
return head1
197+
else:
198+
head2.next = self.mergeTwoLists(head1, head2.next)
199+
return head2
131200

132-
# Merge the remaining nodes
133-
list1.next = self.mergeTwoLists(
134-
list1.next,
135-
list2
136-
)
137201

138-
return list1
202+
# =========================================================
203+
# Complexity Analysis
204+
# =========================================================
205+
"""
206+
Approach Time Auxiliary Space
207+
----------------------------------------------------------
208+
Brute Force O(m+n) O(m+n)
209+
Optimal Iterative O(m+n) O(1)
210+
Recursive O(m+n) O(m+n)
211+
212+
where,
213+
m = length of first linked list
214+
n = length of second linked list
215+
"""
139216

140-
else:
141217

142-
# Merge the remaining nodes
143-
list2.next = self.mergeTwoLists(
144-
list1,
145-
list2.next
146-
)
218+
# =========================================================
219+
# Key Interview Points
220+
# =========================================================
221+
"""
222+
✓ Dummy node avoids handling special cases for the head.
223+
224+
✓ The iterative approach is the most optimal because:
225+
- Every node is visited exactly once.
226+
- No extra linked list is created.
227+
- No recursion stack is used.
228+
229+
✓ The brute-force approach is easier to understand but
230+
requires additional memory.
231+
232+
✓ The recursive solution is elegant but uses O(m+n)
233+
auxiliary space due to the recursion stack.
147234
148-
return list2
235+
✓ The iterative in-place approach is the solution most
236+
interviewers expect.
237+
"""

0 commit comments

Comments
 (0)