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