Skip to content

Commit 1e93c54

Browse files
authored
Concatenated merge sort, iterative merge sort and insertion merge sort .Fixes TheAlgorithms#8098
1 parent 9190888 commit 1e93c54

File tree

1 file changed

+214
-17
lines changed

1 file changed

+214
-17
lines changed

Diff for: sorts/merge_sort.py

+214-17
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,204 @@
11
"""
2-
This is a pure Python implementation of the merge sort algorithm.
3-
4-
For doctests run following command:
5-
python -m doctest -v merge_sort.py
6-
or
7-
python3 -m doctest -v merge_sort.py
8-
For manual testing run:
9-
python merge_sort.py
2+
For doctests, run the following command:
3+
python -m doctest -v merge_sort.py
4+
or
5+
python3 -m doctest -v merge_sort.py
6+
7+
For manual testing, run:
8+
python merge_sort.py
109
"""
10+
from __future__ import annotations
11+
import itertools
12+
import doctest
13+
import sys
14+
15+
def binary_search_insertion(sorted_list: list[int], item: int) -> list[int]:
16+
"""
17+
Inserts an item into a sorted list while maintaining order.
1118
19+
>>> binary_search_insertion([1, 2, 7, 9, 10], 4)
20+
[1, 2, 4, 7, 9, 10]
21+
"""
22+
left = 0
23+
right = len(sorted_list) - 1
24+
while left <= right:
25+
middle = (left + right) // 2
26+
if left == right:
27+
if sorted_list[middle] < item:
28+
left = middle + 1
29+
break
30+
elif sorted_list[middle] < item:
31+
left = middle + 1
32+
else:
33+
right = middle - 1
34+
sorted_list.insert(left, item)
35+
return sorted_list
1236

13-
def merge_sort(collection: list) -> list:
37+
def merge(left: list[list[int]], right: list[list[int]]) -> list[list[int]]:
38+
"""
39+
Merges two sorted lists into a single sorted list.
40+
41+
>>> merge([[1, 6], [9, 10]], [[2, 3], [4, 5], [7, 8]])
42+
[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
43+
"""
44+
result = []
45+
while left and right:
46+
if left[0][0] < right[0][0]:
47+
result.append(left.pop(0))
48+
else:
49+
result.append(right.pop(0))
50+
return result + left + right
51+
52+
def sortlist_2d(list_2d: list[list[int]]) -> list[list[int]]:
53+
"""
54+
Sorts a 2D list based on the first element of each sublist.
55+
56+
>>> sortlist_2d([[9, 10], [1, 6], [7, 8], [2, 3], [4, 5]])
57+
[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
58+
"""
59+
length = len(list_2d)
60+
if length <= 1:
61+
return list_2d
62+
middle = length // 2
63+
return merge(sortlist_2d(list_2d[:middle]), sortlist_2d(list_2d[middle:]))
64+
65+
def merge_insertion_sort(collection: list[int]) -> list[int]:
66+
"""Pure implementation of merge-insertion sort algorithm in Python
67+
68+
:param collection: some mutable ordered collection with comparable items inside
69+
:return: the same collection ordered by ascending
70+
71+
Examples:
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+
81+
Testing 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)
85+
True
86+
"""
87+
88+
if len(collection) <= 1:
89+
return collection
90+
91+
two_paired_list = []
92+
has_last_odd_item = False
93+
for i in range(0, len(collection), 2):
94+
if i == len(collection) - 1:
95+
has_last_odd_item = True
96+
else:
97+
if collection[i] < collection[i + 1]:
98+
two_paired_list.append([collection[i], collection[i + 1]])
99+
else:
100+
two_paired_list.append([collection[i + 1], collection[i]])
101+
102+
sorted_list_2d = sortlist_2d(two_paired_list)
103+
result = [i[0] for i in sorted_list_2d]
104+
result.append(sorted_list_2d[-1][1])
105+
106+
if has_last_odd_item:
107+
pivot = collection[-1]
108+
result = binary_search_insertion(result, pivot)
109+
110+
is_last_odd_item_inserted_before_this_index = False
111+
for i in range(len(sorted_list_2d) - 1):
112+
if result[i] == collection[-1] and has_last_odd_item:
113+
is_last_odd_item_inserted_before_this_index = True
114+
pivot = sorted_list_2d[i][1]
115+
if is_last_odd_item_inserted_before_this_index:
116+
result = result[: i + 2] + binary_search_insertion(result[i + 2 :], pivot)
117+
else:
118+
result = result[: i + 1] + binary_search_insertion(result[i + 1 :], pivot)
119+
120+
return result
121+
122+
def merge_iter(input_list: list[int], low: int, mid: int, high: int) -> list[int]:
123+
"""
124+
Sorts the left-half and right-half individually then merges them into a result.
125+
126+
:param input_list: List to be sorted.
127+
:param low: Starting index of the segment.
128+
:param mid: Middle index to split the segment.
129+
:param high: Ending index of the segment.
130+
:return: Merged sorted list.
131+
"""
132+
left = input_list[low:mid + 1]
133+
right = input_list[mid + 1:high + 1]
134+
result = []
135+
136+
i = j = 0
137+
while i < len(left) and j < len(right):
138+
if left[i] <= right[j]:
139+
result.append(left[i])
140+
i += 1
141+
else:
142+
result.append(right[j])
143+
j += 1
144+
145+
result.extend(left[i:])
146+
result.extend(right[j:])
147+
148+
input_list[low:high + 1] = result
149+
return input_list
150+
151+
def iter_merge_sort(input_list: list[int]) -> list[int]:
152+
"""
153+
Return a sorted copy of the input list using iterative merge sort.
154+
155+
>>> iter_merge_sort([5, 9, 8, 7, 1, 2, 7])
156+
[1, 2, 5, 7, 7, 8, 9]
157+
>>> iter_merge_sort([1])
158+
[1]
159+
>>> iter_merge_sort([2, 1])
160+
[1, 2]
161+
>>> iter_merge_sort([2, 1, 3])
162+
[1, 2, 3]
163+
>>> iter_merge_sort([4, 3, 2, 1])
164+
[1, 2, 3, 4]
165+
>>> iter_merge_sort([5, 4, 3, 2, 1])
166+
[1, 2, 3, 4, 5]
167+
>>> iter_merge_sort(['c', 'b', 'a'])
168+
['a', 'b', 'c']
169+
>>> iter_merge_sort([0.3, 0.2, 0.1])
170+
[0.1, 0.2, 0.3]
171+
>>> iter_merge_sort(['dep', 'dang', 'trai'])
172+
['dang', 'dep', 'trai']
173+
>>> iter_merge_sort([6])
174+
[6]
175+
>>> iter_merge_sort([])
176+
[]
177+
>>> iter_merge_sort([-2, -9, -1, -4])
178+
[-9, -4, -2, -1]
179+
>>> iter_merge_sort([1.1, 1, 0.0, -1, -1.1])
180+
[-1.1, -1, 0.0, 1, 1.1]
181+
"""
182+
if not isinstance(input_list, list):
183+
raise TypeError("Input must be a list")
184+
185+
n = len(input_list)
186+
if n <= 1:
187+
return input_list
188+
189+
width = 1
190+
while width < n:
191+
for i in range(0, n, 2 * width):
192+
low = i
193+
mid = min(i + width - 1, n - 1)
194+
high = min(i + 2 * width - 1, n - 1)
195+
if mid < high:
196+
merge_iter(input_list, low, mid, high)
197+
width *= 2
198+
199+
return input_list
200+
201+
def merge_sort(collection: list[int]) -> list[int]:
14202
"""
15203
Sorts a list using the merge sort algorithm.
16204
@@ -19,16 +207,14 @@ def merge_sort(collection: list) -> list:
19207
20208
Time Complexity: O(n log n)
21209
22-
Examples:
23210
>>> merge_sort([0, 5, 3, 2, 2])
24211
[0, 2, 2, 3, 5]
25212
>>> merge_sort([])
26213
[]
27214
>>> merge_sort([-2, -5, -45])
28215
[-45, -5, -2]
29216
"""
30-
31-
def merge(left: list, right: list) -> list:
217+
def merge(left: list[int], right: list[int]) -> list[int]:
32218
"""
33219
Merge two sorted lists into a single sorted list.
34220
@@ -48,16 +234,27 @@ def merge(left: list, right: list) -> list:
48234
mid_index = len(collection) // 2
49235
return merge(merge_sort(collection[:mid_index]), merge_sort(collection[mid_index:]))
50236

51-
52237
if __name__ == "__main__":
53-
import doctest
54238

55-
doctest.testmod()
239+
if not hasattr(sys, 'gettrace') or sys.gettrace() is None:
240+
doctest.testmod() # Only run tests if not debugging
56241

57242
try:
58243
user_input = input("Enter numbers separated by a comma:\n").strip()
59244
unsorted = [int(item) for item in user_input.split(",")]
60-
sorted_list = merge_sort(unsorted)
61-
print(*sorted_list, sep=",")
245+
246+
sorted_list_merge_sort = merge_sort(unsorted)
247+
sorted_list_iter_merge_sort = iter_merge_sort(unsorted.copy()) # Use copy to avoid sorting the same list twice
248+
sorted_list_merge_insertion_sort = merge_insertion_sort(unsorted.copy())
249+
250+
print(f"Unsorted list: {unsorted}")
251+
print(f"Sorted list using merge_sort: {sorted_list_merge_sort}")
252+
print(f"Sorted list using iter_merge_sort: {sorted_list_iter_merge_sort}")
253+
print(f"Sorted list using merge_insertion_sort: {sorted_list_merge_insertion_sort}")
254+
255+
print("Merge Sort Result:", *sorted_list_merge_sort, sep=",")
256+
print("Iterative Merge Sort Result:", *sorted_list_iter_merge_sort, sep=",")
257+
print("Merge Insertion Sort Result:", *sorted_list_merge_insertion_sort, sep=",")
62258
except ValueError:
63259
print("Invalid input. Please enter valid integers separated by commas.")
260+

0 commit comments

Comments
 (0)