TheAlgorithms-Python
200 строк · 5.5 Кб
1"""
2This is a pure Python implementation of the merge-insertion sort algorithm
3Source: https://en.wikipedia.org/wiki/Merge-insertion_sort
4
5For doctests run following command:
6python3 -m doctest -v merge_insertion_sort.py
7or
8python -m doctest -v merge_insertion_sort.py
9
10For manual testing run:
11python3 merge_insertion_sort.py
12"""
13
14from __future__ import annotations15
16
17def binary_search_insertion(sorted_list, item):18"""19>>> binary_search_insertion([1, 2, 7, 9, 10], 4)
20[1, 2, 4, 7, 9, 10]
21"""
22left = 023right = len(sorted_list) - 124while left <= right:25middle = (left + right) // 226if left == right:27if sorted_list[middle] < item:28left = middle + 129break30elif sorted_list[middle] < item:31left = middle + 132else:33right = middle - 134sorted_list.insert(left, item)35return sorted_list36
37
38def merge(left, right):39"""40>>> merge([[1, 6], [9, 10]], [[2, 3], [4, 5], [7, 8]])
41[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
42"""
43result = []44while left and right:45if left[0][0] < right[0][0]:46result.append(left.pop(0))47else:48result.append(right.pop(0))49return result + left + right50
51
52def sortlist_2d(list_2d):53"""54>>> sortlist_2d([[9, 10], [1, 6], [7, 8], [2, 3], [4, 5]])
55[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
56"""
57length = len(list_2d)58if length <= 1:59return list_2d60middle = length // 261return merge(sortlist_2d(list_2d[:middle]), sortlist_2d(list_2d[middle:]))62
63
64def merge_insertion_sort(collection: list[int]) -> list[int]:65"""Pure implementation of merge-insertion sort algorithm in Python66
67:param collection: some mutable ordered collection with heterogeneous
68comparable items inside
69:return: the same collection ordered by ascending
70
71Examples:
72>>> merge_insertion_sort([0, 5, 3, 2, 2])
73[0, 2, 2, 3, 5]
74
75>>> merge_insertion_sort([99])
76[99]
77
78>>> merge_insertion_sort([-2, -5, -45])
79[-45, -5, -2]
80
81Testing with all permutations on range(0,5):
82>>> import itertools
83>>> permutations = list(itertools.permutations([0, 1, 2, 3, 4]))
84>>> all(merge_insertion_sort(p) == [0, 1, 2, 3, 4] for p in permutations)
85True
86"""
87
88if len(collection) <= 1:89return collection90
91"""92Group the items into two pairs, and leave one element if there is a last odd item.
93
94Example: [999, 100, 75, 40, 10000]
95-> [999, 100], [75, 40]. Leave 10000.
96"""
97two_paired_list = []98has_last_odd_item = False99for i in range(0, len(collection), 2):100if i == len(collection) - 1:101has_last_odd_item = True102else:103"""104Sort two-pairs in each groups.
105
106Example: [999, 100], [75, 40]
107-> [100, 999], [40, 75]
108"""
109if collection[i] < collection[i + 1]:110two_paired_list.append([collection[i], collection[i + 1]])111else:112two_paired_list.append([collection[i + 1], collection[i]])113
114"""115Sort two_paired_list.
116
117Example: [100, 999], [40, 75]
118-> [40, 75], [100, 999]
119"""
120sorted_list_2d = sortlist_2d(two_paired_list)121
122"""12340 < 100 is sure because it has already been sorted.
124Generate the sorted_list of them so that you can avoid unnecessary comparison.
125
126Example:
127group0 group1
12840 100
12975 999
130->
131group0 group1
132[40, 100]
13375 999
134"""
135result = [i[0] for i in sorted_list_2d]136
137"""138100 < 999 is sure because it has already been sorted.
139Put 999 in last of the sorted_list so that you can avoid unnecessary comparison.
140
141Example:
142group0 group1
143[40, 100]
14475 999
145->
146group0 group1
147[40, 100, 999]
14875
149"""
150result.append(sorted_list_2d[-1][1])151
152"""153Insert the last odd item left if there is.
154
155Example:
156group0 group1
157[40, 100, 999]
15875
159->
160group0 group1
161[40, 100, 999, 10000]
16275
163"""
164if has_last_odd_item:165pivot = collection[-1]166result = binary_search_insertion(result, pivot)167
168"""169Insert the remaining items.
170In this case, 40 < 75 is sure because it has already been sorted.
171Therefore, you only need to insert 75 into [100, 999, 10000],
172so that you can avoid unnecessary comparison.
173
174Example:
175group0 group1
176[40, 100, 999, 10000]
177^ You don't need to compare with this as 40 < 75 is already sure.
17875
179->
180[40, 75, 100, 999, 10000]
181"""
182is_last_odd_item_inserted_before_this_index = False183for i in range(len(sorted_list_2d) - 1):184if result[i] == collection[-1] and has_last_odd_item:185is_last_odd_item_inserted_before_this_index = True186pivot = sorted_list_2d[i][1]187# If last_odd_item is inserted before the item's index,188# you should forward index one more.189if is_last_odd_item_inserted_before_this_index:190result = result[: i + 2] + binary_search_insertion(result[i + 2 :], pivot)191else:192result = result[: i + 1] + binary_search_insertion(result[i + 1 :], pivot)193
194return result195
196
197if __name__ == "__main__":198user_input = input("Enter numbers separated by a comma:\n").strip()199unsorted = [int(item) for item in user_input.split(",")]200print(merge_insertion_sort(unsorted))201