|
| 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) |
0 commit comments