|
| 1 | +""" |
| 2 | +Introspective Sort is hybrid sort (Quick Sort + Heap Sort + Insertion Sort) |
| 3 | +if the size of the list is under 16, use insertion sort |
| 4 | +https://en.wikipedia.org/wiki/Introsort |
| 5 | +""" |
| 6 | +import math |
| 7 | + |
| 8 | + |
| 9 | +def insertion_sort(array: list, start: int = 0, end: int = 0) -> list: |
| 10 | + """ |
| 11 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 12 | +
|
| 13 | + >>> insertion_sort(array, 0, len(array)) |
| 14 | + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] |
| 15 | + """ |
| 16 | + end = end or len(array) |
| 17 | + for i in range(start, end): |
| 18 | + temp_index = i |
| 19 | + temp_index_value = array[i] |
| 20 | + while temp_index != start and temp_index_value < array[temp_index - 1]: |
| 21 | + array[temp_index] = array[temp_index - 1] |
| 22 | + temp_index -= 1 |
| 23 | + array[temp_index] = temp_index_value |
| 24 | + return array |
| 25 | + |
| 26 | + |
| 27 | +def heapify(array: list, index: int, heap_size: int) -> None: # Max Heap |
| 28 | + """ |
| 29 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 30 | +
|
| 31 | + >>> heapify(array, len(array) // 2 ,len(array)) |
| 32 | + """ |
| 33 | + largest = index |
| 34 | + left_index = 2 * index + 1 # Left Node |
| 35 | + right_index = 2 * index + 2 # Right Node |
| 36 | + |
| 37 | + if left_index < heap_size and array[largest] < array[left_index]: |
| 38 | + largest = left_index |
| 39 | + |
| 40 | + if right_index < heap_size and array[largest] < array[right_index]: |
| 41 | + largest = right_index |
| 42 | + |
| 43 | + if largest != index: |
| 44 | + array[index], array[largest] = array[largest], array[index] |
| 45 | + heapify(array, largest, heap_size) |
| 46 | + |
| 47 | + |
| 48 | +def heap_sort(array: list) -> list: |
| 49 | + """ |
| 50 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 51 | +
|
| 52 | + >>> heap_sort(array) |
| 53 | + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] |
| 54 | + """ |
| 55 | + n = len(array) |
| 56 | + |
| 57 | + for i in range(n // 2, -1, -1): |
| 58 | + heapify(array, i, n) |
| 59 | + |
| 60 | + for i in range(n - 1, 0, -1): |
| 61 | + array[i], array[0] = array[0], array[i] |
| 62 | + heapify(array, 0, i) |
| 63 | + |
| 64 | + return array |
| 65 | + |
| 66 | + |
| 67 | +def median_of_3( |
| 68 | + array: list, first_index: int, middle_index: int, last_index: int |
| 69 | +) -> int: |
| 70 | + """ |
| 71 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 72 | +
|
| 73 | + >>> median_of_3(array, 0, 0 + ((len(array) - 0) // 2) + 1, len(array) - 1) |
| 74 | + 12 |
| 75 | + """ |
| 76 | + if (array[first_index] > array[middle_index]) != ( |
| 77 | + array[first_index] > array[last_index] |
| 78 | + ): |
| 79 | + return array[first_index] |
| 80 | + elif (array[middle_index] > array[first_index]) != ( |
| 81 | + array[middle_index] > array[last_index] |
| 82 | + ): |
| 83 | + return array[middle_index] |
| 84 | + else: |
| 85 | + return array[last_index] |
| 86 | + |
| 87 | + |
| 88 | +def partition(array: list, low: int, high: int, pivot: int) -> int: |
| 89 | + """ |
| 90 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 91 | +
|
| 92 | + >>> partition(array, 0, len(array), 12) |
| 93 | + 8 |
| 94 | + """ |
| 95 | + i = low |
| 96 | + j = high |
| 97 | + while True: |
| 98 | + while array[i] < pivot: |
| 99 | + i += 1 |
| 100 | + j -= 1 |
| 101 | + while pivot < array[j]: |
| 102 | + j -= 1 |
| 103 | + if i >= j: |
| 104 | + return i |
| 105 | + array[i], array[j] = array[j], array[i] |
| 106 | + i += 1 |
| 107 | + |
| 108 | + |
| 109 | +def sort(array: list) -> list: |
| 110 | + """ |
| 111 | + :param collection: some mutable ordered collection with heterogeneous |
| 112 | + comparable items inside |
| 113 | + :return: the same collection ordered by ascending |
| 114 | +
|
| 115 | + Examples: |
| 116 | + >>> sort([4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12]) |
| 117 | + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] |
| 118 | +
|
| 119 | + >>> sort([-1, -5, -3, -13, -44]) |
| 120 | + [-44, -13, -5, -3, -1] |
| 121 | +
|
| 122 | + >>> sort([]) |
| 123 | + [] |
| 124 | +
|
| 125 | + >>> sort([5]) |
| 126 | + [5] |
| 127 | +
|
| 128 | + >>> sort([-3, 0, -7, 6, 23, -34]) |
| 129 | + [-34, -7, -3, 0, 6, 23] |
| 130 | +
|
| 131 | + >>> sort([1.7, 1.0, 3.3, 2.1, 0.3 ]) |
| 132 | + [0.3, 1.0, 1.7, 2.1, 3.3] |
| 133 | +
|
| 134 | + >>> sort(['d', 'a', 'b', 'e', 'c']) |
| 135 | + ['a', 'b', 'c', 'd', 'e'] |
| 136 | + """ |
| 137 | + if len(array) == 0: |
| 138 | + return array |
| 139 | + max_depth = 2 * math.ceil(math.log2(len(array))) |
| 140 | + size_threshold = 16 |
| 141 | + return intro_sort(array, 0, len(array), size_threshold, max_depth) |
| 142 | + |
| 143 | + |
| 144 | +def intro_sort( |
| 145 | + array: list, start: int, end: int, size_threshold: int, max_depth: int |
| 146 | +) -> list: |
| 147 | + """ |
| 148 | + >>> array = [4, 2, 6, 8, 1, 7, 8, 22, 14, 56, 27, 79, 23, 45, 14, 12] |
| 149 | +
|
| 150 | + >>> max_depth = 2 * math.ceil(math.log2(len(array))) |
| 151 | +
|
| 152 | + >>> intro_sort(array, 0, len(array), 16, max_depth) |
| 153 | + [1, 2, 4, 6, 7, 8, 8, 12, 14, 14, 22, 23, 27, 45, 56, 79] |
| 154 | + """ |
| 155 | + while end - start > size_threshold: |
| 156 | + if max_depth == 0: |
| 157 | + return heap_sort(array) |
| 158 | + max_depth -= 1 |
| 159 | + pivot = median_of_3(array, start, start + ((end - start) // 2) + 1, end - 1) |
| 160 | + p = partition(array, start, end, pivot) |
| 161 | + intro_sort(array, p, end, size_threshold, max_depth) |
| 162 | + end = p |
| 163 | + return insertion_sort(array, start, end) |
| 164 | + |
| 165 | + |
| 166 | +if __name__ == "__main__": |
| 167 | + import doctest |
| 168 | + |
| 169 | + doctest.testmod() |
| 170 | + |
| 171 | + user_input = input("Enter numbers separated by a comma : ").strip() |
| 172 | + unsorted = [float(item) for item in user_input.split(",")] |
| 173 | + print(sort(unsorted)) |
0 commit comments