|
7 | 7 | For manual testing run: |
8 | 8 | python3 quick_sort.py |
9 | 9 | """ |
10 | | - |
11 | 10 | from __future__ import annotations |
12 | 11 |
|
13 | 12 | from random import randrange |
14 | 13 |
|
15 | 14 |
|
16 | 15 | def quick_sort(collection: list) -> list: |
17 | | - """A pure Python implementation of quicksort algorithm. |
| 16 | + """A pure Python implementation of quicksort algorithm using in-place sorting. |
18 | 17 |
|
19 | 18 | :param collection: a mutable collection of comparable items |
20 | 19 | :return: the same collection ordered in ascending order |
21 | 20 |
|
22 | 21 | Examples: |
23 | 22 | >>> quick_sort([0, 5, 3, 2, 2]) |
24 | 23 | [0, 2, 2, 3, 5] |
| 24 | +
|
25 | 25 | >>> quick_sort([]) |
26 | 26 | [] |
| 27 | +
|
27 | 28 | >>> quick_sort([-2, 5, 0, -45]) |
28 | 29 | [-45, -2, 0, 5] |
29 | 30 | """ |
30 | | - # Base case: if the collection has 0 or 1 elements, it is already sorted |
31 | | - if len(collection) < 2: |
32 | | - return collection |
| 31 | + # Call the helper function to sort in-place |
| 32 | + _quick_sort(collection, 0, len(collection) - 1) |
| 33 | + return collection |
| 34 | + |
| 35 | + |
| 36 | +def _quick_sort(collection: list, low: int, high: int) -> None: |
| 37 | + """Helper function that performs in-place quicksort. |
| 38 | +
|
| 39 | + :param collection: the list to sort |
| 40 | + :param low: starting index of the partition |
| 41 | + :param high: ending index of the partition |
| 42 | + """ |
| 43 | + if low < high: |
| 44 | + # Partition the array and get the pivot index |
| 45 | + pivot_index = _partition(collection, low, high) |
| 46 | + # Recursively sort elements before and after partition |
| 47 | + _quick_sort(collection, low, pivot_index - 1) |
| 48 | + _quick_sort(collection, pivot_index + 1, high) |
| 49 | + |
| 50 | + |
| 51 | +def _partition(collection: list, low: int, high: int) -> int: |
| 52 | + """In-place partitioning using Lomuto partition scheme. |
| 53 | +
|
| 54 | + :param collection: the list to partition |
| 55 | + :param low: starting index of the partition |
| 56 | + :param high: ending index of the partition |
| 57 | + :return: the final pivot index after partitioning |
| 58 | + """ |
| 59 | + # Randomly select a pivot index and swap with the last element |
| 60 | + pivot_index = randrange(low, high + 1) |
| 61 | + collection[pivot_index], collection[high] = collection[high], collection[pivot_index] |
| 62 | + pivot = collection[high] |
33 | 63 |
|
34 | | - # Randomly select a pivot index and remove the pivot element from the collection |
35 | | - pivot_index = randrange(len(collection)) |
36 | | - pivot = collection.pop(pivot_index) |
| 64 | + # Index of smaller element (elements <= pivot will be moved to left of this) |
| 65 | + i = low - 1 |
37 | 66 |
|
38 | | - # Partition the remaining elements into two groups: lesser or equal, and greater |
39 | | - lesser = [item for item in collection if item <= pivot] |
40 | | - greater = [item for item in collection if item > pivot] |
| 67 | + # Traverse through all elements and move smaller elements to the left |
| 68 | + for j in range(low, high): |
| 69 | + if collection[j] <= pivot: |
| 70 | + i += 1 |
| 71 | + collection[i], collection[j] = collection[j], collection[i] |
41 | 72 |
|
42 | | - # Recursively sort the lesser and greater groups, and combine with the pivot |
43 | | - return [*quick_sort(lesser), pivot, *quick_sort(greater)] |
| 73 | + # Place pivot in its correct position |
| 74 | + collection[i + 1], collection[high] = collection[high], collection[i + 1] |
| 75 | + return i + 1 |
44 | 76 |
|
45 | 77 |
|
46 | 78 | if __name__ == "__main__": |
47 | | - # Get user input and convert it into a list of integers |
48 | | - user_input = input("Enter numbers separated by a comma:\n").strip() |
49 | | - unsorted = [int(item) for item in user_input.split(",")] |
| 79 | + import doctest |
50 | 80 |
|
51 | | - # Print the result of sorting the user-provided list |
52 | | - print(quick_sort(unsorted)) |
| 81 | + doctest.testmod() |
0 commit comments