Skip to content

Commit bd71672

Browse files
feat: add problem 148 sort list
1 parent 23a61e2 commit bd71672

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

explanations/148/en.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# 148. Sort List
2+
3+
**Difficulty:** Medium
4+
**Link:** https://leetcode.com/problems/sort-list/
5+
6+
## Problem Description
7+
8+
Given the `head` of a linked list, return *the list after sorting it in **ascending order***.
9+
10+
**Example 1:**
11+
```
12+
Input: head = [4,2,1,3]
13+
Output: [1,2,3,4]
14+
```
15+
16+
**Example 2:**
17+
```
18+
Input: head = [-1,5,3,4,0]
19+
Output: [-1,0,3,4,5]
20+
```
21+
22+
**Example 3:**
23+
```
24+
Input: head = []
25+
Output: []
26+
```
27+
28+
**Constraints:**
29+
- The number of nodes in the list is in the range `[0, 5 * 10^4]`.
30+
- `-10^5 <= Node.val <= 10^5`
31+
32+
**Follow up:** Can you sort the linked list in `O(n logn)` time and `O(1)` memory (i.e. constant space)?
33+
34+
## Explanation
35+
36+
### Strategy
37+
38+
This is a **divide-and-conquer problem** that requires sorting a linked list in O(n log n) time and O(1) space. The key insight is to use merge sort, which naturally works well with linked lists and can be implemented in-place.
39+
40+
**Key observations:**
41+
- We need O(n log n) time complexity for optimal sorting
42+
- We need O(1) space complexity (no extra data structures)
43+
- Merge sort is perfect for linked lists because we can split and merge in-place
44+
- We can use the fast/slow pointer technique to find the middle
45+
46+
**High-level approach:**
47+
1. **Find the middle**: Use fast/slow pointers to split the list
48+
2. **Recursively sort**: Sort the left and right halves
49+
3. **Merge sorted lists**: Merge the two sorted halves
50+
4. **Return result**: Return the merged sorted list
51+
52+
### Steps
53+
54+
Let's break down the solution step by step:
55+
56+
**Step 1: Handle base cases**
57+
- If head is null or head.next is null, return head (already sorted)
58+
59+
**Step 2: Find the middle of the list**
60+
- Use fast/slow pointer technique
61+
- Slow pointer moves 1 step, fast pointer moves 2 steps
62+
- When fast reaches end, slow is at middle
63+
64+
**Step 3: Split the list**
65+
- Set `mid = slow.next`
66+
- Set `slow.next = None` to split the list
67+
68+
**Step 4: Recursively sort halves**
69+
- Sort the left half: `left = sortList(head)`
70+
- Sort the right half: `right = sortList(mid)`
71+
72+
**Step 5: Merge the sorted halves**
73+
- Use a dummy node to simplify merging
74+
- Compare nodes from both lists and link them in order
75+
76+
**Example walkthrough:**
77+
Let's trace through the first example:
78+
79+
```
80+
head = [4,2,1,3]
81+
82+
Step 1: Find middle
83+
slow = 4, fast = 4
84+
slow = 2, fast = 1
85+
slow = 1, fast = 3
86+
slow = 3, fast = null
87+
middle = 3
88+
89+
Step 2: Split list
90+
left = [4,2,1], right = [3]
91+
92+
Step 3: Recursively sort left
93+
left = [4,2,1] → [1,2,4]
94+
95+
Step 4: Recursively sort right
96+
right = [3] → [3]
97+
98+
Step 5: Merge [1,2,4] and [3]
99+
result = [1,2,3,4]
100+
```
101+
102+
> **Note:** Merge sort is ideal for linked lists because we can split and merge in-place without extra space. The fast/slow pointer technique efficiently finds the middle, and the merge step can be done by simply relinking nodes.
103+
104+
### Solution
105+
106+
```python
107+
# Definition for singly-linked list.
108+
# class ListNode:
109+
# def __init__(self, val=0, next=None):
110+
# self.val = val
111+
# self.next = next
112+
113+
class Solution:
114+
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
115+
# Handle base cases
116+
if not head or not head.next:
117+
return head
118+
119+
# Find the middle of the list
120+
slow = head
121+
fast = head.next
122+
123+
while fast and fast.next:
124+
slow = slow.next
125+
fast = fast.next.next
126+
127+
# Split the list
128+
mid = slow.next
129+
slow.next = None
130+
131+
# Recursively sort the two halves
132+
left = self.sortList(head)
133+
right = self.sortList(mid)
134+
135+
# Merge the sorted halves
136+
return self.merge(left, right)
137+
138+
def merge(self, left: Optional[ListNode], right: Optional[ListNode]) -> Optional[ListNode]:
139+
# Create a dummy node to simplify merging
140+
dummy = ListNode(0)
141+
current = dummy
142+
143+
# Merge the two sorted lists
144+
while left and right:
145+
if left.val <= right.val:
146+
current.next = left
147+
left = left.next
148+
else:
149+
current.next = right
150+
right = right.next
151+
current = current.next
152+
153+
# Attach remaining nodes
154+
if left:
155+
current.next = left
156+
if right:
157+
current.next = right
158+
159+
# Return the merged list (skip dummy node)
160+
return dummy.next
161+
```
162+
163+
**Time Complexity:** O(n log n) - merge sort time complexity
164+
**Space Complexity:** O(log n) - recursion stack space (not O(1) due to recursion)

solutions/148/01.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Definition for singly-linked list.
2+
# class ListNode:
3+
# def __init__(self, val=0, next=None):
4+
# self.val = val
5+
# self.next = next
6+
7+
8+
class Solution:
9+
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
10+
# Handle base cases
11+
if not head or not head.next:
12+
return head
13+
14+
# Find the middle of the list
15+
slow = head
16+
fast = head.next
17+
18+
while fast and fast.next:
19+
slow = slow.next
20+
fast = fast.next.next
21+
22+
# Split the list
23+
mid = slow.next
24+
slow.next = None
25+
26+
# Recursively sort the two halves
27+
left = self.sortList(head)
28+
right = self.sortList(mid)
29+
30+
# Merge the sorted halves
31+
return self.merge(left, right)
32+
33+
def merge(
34+
self, left: Optional[ListNode], right: Optional[ListNode]
35+
) -> Optional[ListNode]:
36+
# Create a dummy node to simplify merging
37+
dummy = ListNode(0)
38+
current = dummy
39+
40+
# Merge the two sorted lists
41+
while left and right:
42+
if left.val <= right.val:
43+
current.next = left
44+
left = left.next
45+
else:
46+
current.next = right
47+
right = right.next
48+
current = current.next
49+
50+
# Attach remaining nodes
51+
if left:
52+
current.next = left
53+
if right:
54+
current.next = right
55+
56+
# Return the merged list (skip dummy node)
57+
return dummy.next

0 commit comments

Comments
 (0)