|
1 | 1 | """ |
2 | | -A pure Python implementation of the quick sort algorithm |
| 2 | +A pure Python implementation of the quick sort algorithm with optimizations |
3 | 3 |
|
4 | 4 | For doctests run following command: |
5 | 5 | python3 -m doctest -v quick_sort.py |
|
13 | 13 | from random import randrange |
14 | 14 |
|
15 | 15 |
|
| 16 | +def insertion_sort(collection: list, left: int, right: int) -> None: |
| 17 | + """Insertion sort for small arrays (optimization for quicksort). |
| 18 | + |
| 19 | + Args: |
| 20 | + collection: List to sort |
| 21 | + left: Starting index |
| 22 | + right: Ending index (inclusive) |
| 23 | + """ |
| 24 | + for i in range(left + 1, right + 1): |
| 25 | + key = collection[i] |
| 26 | + j = i - 1 |
| 27 | + while j >= left and collection[j] > key: |
| 28 | + collection[j + 1] = collection[j] |
| 29 | + j -= 1 |
| 30 | + collection[j + 1] = key |
| 31 | + |
| 32 | + |
| 33 | +def median_of_three(collection: list, left: int, mid: int, right: int) -> int: |
| 34 | + """Return the index of the median of three elements. |
| 35 | + |
| 36 | + Args: |
| 37 | + collection: List to find median in |
| 38 | + left: Left index |
| 39 | + mid: Middle index |
| 40 | + right: Right index |
| 41 | + |
| 42 | + Returns: |
| 43 | + Index of the median element |
| 44 | + """ |
| 45 | + a, b, c = collection[left], collection[mid], collection[right] |
| 46 | + if (a <= b <= c) or (c <= b <= a): |
| 47 | + return mid |
| 48 | + elif (b <= a <= c) or (c <= a <= b): |
| 49 | + return left |
| 50 | + else: |
| 51 | + return right |
| 52 | + |
| 53 | + |
| 54 | +def partition_hoare(collection: list, left: int, right: int) -> int: |
| 55 | + """Hoare partition scheme for quicksort. |
| 56 | + |
| 57 | + Args: |
| 58 | + collection: List to partition |
| 59 | + left: Left boundary |
| 60 | + right: Right boundary |
| 61 | + |
| 62 | + Returns: |
| 63 | + Final position of pivot |
| 64 | + """ |
| 65 | + # Use median of three for better pivot selection |
| 66 | + mid = left + (right - left) // 2 |
| 67 | + pivot_idx = median_of_three(collection, left, mid, right) |
| 68 | + |
| 69 | + # Move pivot to the beginning |
| 70 | + collection[left], collection[pivot_idx] = collection[pivot_idx], collection[left] |
| 71 | + pivot = collection[left] |
| 72 | + |
| 73 | + i, j = left - 1, right + 1 |
| 74 | + |
| 75 | + while True: |
| 76 | + i += 1 |
| 77 | + while collection[i] < pivot: |
| 78 | + i += 1 |
| 79 | + |
| 80 | + j -= 1 |
| 81 | + while collection[j] > pivot: |
| 82 | + j -= 1 |
| 83 | + |
| 84 | + if i >= j: |
| 85 | + return j |
| 86 | + |
| 87 | + collection[i], collection[j] = collection[j], collection[i] |
| 88 | + |
| 89 | + |
| 90 | +def quick_sort_inplace(collection: list, left: int = 0, right: int = None) -> None: |
| 91 | + """Optimized in-place quicksort with multiple optimizations. |
| 92 | + |
| 93 | + Optimizations: |
| 94 | + - In-place partitioning (O(1) extra space vs O(n)) |
| 95 | + - Median-of-three pivot selection |
| 96 | + - Hybrid with insertion sort for small arrays |
| 97 | + - Hoare partition scheme (better for duplicates) |
| 98 | + |
| 99 | + Args: |
| 100 | + collection: List to sort (modified in-place) |
| 101 | + left: Left boundary |
| 102 | + right: Right boundary |
| 103 | + """ |
| 104 | + if right is None: |
| 105 | + right = len(collection) - 1 |
| 106 | + |
| 107 | + if left < right: |
| 108 | + # Use insertion sort for small arrays (optimization) |
| 109 | + if right - left < 10: |
| 110 | + insertion_sort(collection, left, right) |
| 111 | + return |
| 112 | + |
| 113 | + # Partition and get pivot position |
| 114 | + pivot_pos = partition_hoare(collection, left, right) |
| 115 | + |
| 116 | + # Recursively sort left and right partitions |
| 117 | + quick_sort_inplace(collection, left, pivot_pos) |
| 118 | + quick_sort_inplace(collection, pivot_pos + 1, right) |
| 119 | + |
| 120 | + |
16 | 121 | def quick_sort(collection: list) -> list: |
17 | 122 | """A pure Python implementation of quicksort algorithm. |
| 123 | + |
| 124 | + This is the original implementation kept for compatibility. |
| 125 | + For better performance, use quick_sort_optimized(). |
18 | 126 |
|
19 | 127 | :param collection: a mutable collection of comparable items |
20 | 128 | :return: the same collection ordered in ascending order |
@@ -43,6 +151,40 @@ def quick_sort(collection: list) -> list: |
43 | 151 | return [*quick_sort(lesser), pivot, *quick_sort(greater)] |
44 | 152 |
|
45 | 153 |
|
| 154 | +def quick_sort_optimized(collection: list) -> list: |
| 155 | + """Optimized quicksort with in-place partitioning and multiple optimizations. |
| 156 | + |
| 157 | + Performance improvements: |
| 158 | + - O(1) extra space vs O(n) in original |
| 159 | + - Better pivot selection (median of three) |
| 160 | + - Hybrid with insertion sort for small arrays |
| 161 | + - More efficient partitioning |
| 162 | + |
| 163 | + Args: |
| 164 | + collection: List to sort |
| 165 | + |
| 166 | + Returns: |
| 167 | + Sorted list |
| 168 | + |
| 169 | + Examples: |
| 170 | + >>> quick_sort_optimized([0, 5, 3, 2, 2]) |
| 171 | + [0, 2, 2, 3, 5] |
| 172 | + >>> quick_sort_optimized([]) |
| 173 | + [] |
| 174 | + >>> quick_sort_optimized([-2, 5, 0, -45]) |
| 175 | + [-45, -2, 0, 5] |
| 176 | + >>> quick_sort_optimized([3, 3, 3, 3, 3]) |
| 177 | + [3, 3, 3, 3, 3] |
| 178 | + """ |
| 179 | + if len(collection) < 2: |
| 180 | + return collection |
| 181 | + |
| 182 | + # Create a copy to avoid modifying the original |
| 183 | + result = collection.copy() |
| 184 | + quick_sort_inplace(result) |
| 185 | + return result |
| 186 | + |
| 187 | + |
46 | 188 | if __name__ == "__main__": |
47 | 189 | # Get user input and convert it into a list of integers |
48 | 190 | user_input = input("Enter numbers separated by a comma:\n").strip() |
|
0 commit comments