Skip to content

Commit ddb883b

Browse files
authored
Merge pull request #2 from csana004/csana004-patch-1
Update merge_sort.py
2 parents 81c7c38 + 3278a1d commit ddb883b

File tree

1 file changed

+106
-227
lines changed

1 file changed

+106
-227
lines changed

sorts/merge_sort.py

+106-227
Original file line numberDiff line numberDiff line change
@@ -1,274 +1,153 @@
11
"""
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
2+
Merge Sort Algorithm
3+
====================
64
7-
For manual testing, run:
8-
python merge_sort.py
9-
"""
5+
This module implements the Merge Sort algorithm with three variations:
6+
1. Recursive Merge Sort
7+
2. Iterative Merge Sort
8+
3. Merge Insertion Sort
109
11-
from __future__ import annotations
12-
import itertools
13-
import doctest
14-
import sys
10+
Usage:
11+
- Run this module directly for manual testing.
12+
- Use doctests to verify correctness.
1513
14+
Example:
15+
python merge_sort.py
16+
"""
1617

17-
def binary_search_insertion(sorted_list: list[int], item: int) -> list[int]:
18-
"""
19-
Inserts an item into a sorted list while maintaining order.
18+
from typing import List
2019

21-
>>> binary_search_insertion([1, 2, 7, 9, 10], 4)
22-
[1, 2, 4, 7, 9, 10]
20+
def merge_sort(arr: List[int]) -> List[int]:
2321
"""
24-
left = 0
25-
right = len(sorted_list) - 1
26-
while left <= right:
27-
middle = (left + right) // 2
28-
if left == right:
29-
if sorted_list[middle] < item:
30-
left = middle + 1
31-
break
32-
elif sorted_list[middle] < item:
33-
left = middle + 1
34-
else:
35-
right = middle - 1
36-
sorted_list.insert(left, item)
37-
return sorted_list
22+
Perform merge sort on a list of integers.
3823
24+
Args:
25+
arr: A list of integers.
3926
40-
def merge(left: list[list[int]], right: list[list[int]]) -> list[list[int]]:
41-
"""
42-
Merges two sorted lists into a single sorted list.
27+
Returns:
28+
A sorted list of integers.
4329
44-
>>> merge([[1, 6], [9, 10]], [[2, 3], [4, 5], [7, 8]])
45-
[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
30+
Example:
31+
>>> merge_sort([8, 3, 5, 6])
32+
[3, 5, 6, 8]
4633
"""
47-
result = []
48-
while left and right:
49-
if left[0][0] < right[0][0]:
50-
result.append(left.pop(0))
51-
else:
52-
result.append(right.pop(0))
53-
return result + left + right
34+
if len(arr) > 1:
35+
mid = len(arr) // 2
36+
left_half = arr[:mid]
37+
right_half = arr[mid:]
5438

39+
merge_sort(left_half)
40+
merge_sort(right_half)
5541

56-
def sortlist_2d(list_2d: list[list[int]]) -> list[list[int]]:
57-
"""
58-
Sorts a 2D list based on the first element of each sublist.
42+
i = j = k = 0
5943

60-
>>> sortlist_2d([[9, 10], [1, 6], [7, 8], [2, 3], [4, 5]])
61-
[[1, 6], [2, 3], [4, 5], [7, 8], [9, 10]]
62-
"""
63-
length = len(list_2d)
64-
if length <= 1:
65-
return list_2d
66-
middle = length // 2
67-
return merge(sortlist_2d(list_2d[:middle]), sortlist_2d(list_2d[middle:]))
44+
while i < len(left_half) and j < len(right_half):
45+
if left_half[i] < right_half[j]:
46+
arr[k] = left_half[i]
47+
i += 1
48+
else:
49+
arr[k] = right_half[j]
50+
j += 1
51+
k += 1
6852

53+
while i < len(left_half):
54+
arr[k] = left_half[i]
55+
i += 1
56+
k += 1
6957

70-
def merge_insertion_sort(collection: list[int]) -> list[int]:
71-
"""Pure implementation of merge-insertion sort algorithm in Python
58+
while j < len(right_half):
59+
arr[k] = right_half[j]
60+
j += 1
61+
k += 1
7262

73-
:param collection: some mutable ordered collection with comparable items inside
74-
:return: the same collection ordered by ascending
63+
return arr
7564

76-
Examples:
77-
>>> merge_insertion_sort([0, 5, 3, 2, 2])
78-
[0, 2, 2, 3, 5]
65+
def iterative_merge_sort(arr: List[int]) -> List[int]:
66+
"""
67+
Perform iterative merge sort on a list of integers.
7968
80-
>>> merge_insertion_sort([99])
81-
[99]
69+
Args:
70+
arr: A list of integers.
8271
83-
>>> merge_insertion_sort([-2, -5, -45])
84-
[-45, -5, -2]
72+
Returns:
73+
A sorted list of integers.
8574
86-
Testing with all permutations on range(0,5):
87-
>>> import itertools
88-
>>> permutations = list(itertools.permutations([0, 1, 2, 3, 4]))
89-
>>> all(merge_insertion_sort(p) == [0, 1, 2, 3, 4] for p in permutations)
90-
True
75+
Example:
76+
>>> iterative_merge_sort([8, 3, 5, 6])
77+
[3, 5, 6, 8]
9178
"""
79+
if len(arr) <= 1:
80+
return arr
9281

93-
if len(collection) <= 1:
94-
return collection
82+
width = 1
83+
n = len(arr)
84+
while width < n:
85+
for i in range(0, n, 2 * width):
86+
left = arr[i:i + width]
87+
right = arr[i + width:i + 2 * width]
88+
merged = merge(left, right)
89+
arr[i:i + len(merged)] = merged
90+
width *= 2
9591

96-
two_paired_list = []
97-
has_last_odd_item = False
98-
for i in range(0, len(collection), 2):
99-
if i == len(collection) - 1:
100-
has_last_odd_item = True
101-
else:
102-
if collection[i] < collection[i + 1]:
103-
two_paired_list.append([collection[i], collection[i + 1]])
104-
else:
105-
two_paired_list.append([collection[i + 1], collection[i]])
106-
107-
sorted_list_2d = sortlist_2d(two_paired_list)
108-
result = [i[0] for i in sorted_list_2d]
109-
result.append(sorted_list_2d[-1][1])
110-
111-
if has_last_odd_item:
112-
pivot = collection[-1]
113-
result = binary_search_insertion(result, pivot)
114-
115-
is_last_odd_item_inserted_before_this_index = False
116-
for i in range(len(sorted_list_2d) - 1):
117-
if result[i] == collection[-1] and has_last_odd_item:
118-
is_last_odd_item_inserted_before_this_index = True
119-
pivot = sorted_list_2d[i][1]
120-
if is_last_odd_item_inserted_before_this_index:
121-
result = result[: i + 2] + binary_search_insertion(result[i + 2 :], pivot)
122-
else:
123-
result = result[: i + 1] + binary_search_insertion(result[i + 1 :], pivot)
92+
return arr
12493

125-
return result
94+
def merge(left: List[int], right: List[int]) -> List[int]:
95+
"""
96+
Merge two sorted lists into a single sorted list.
12697
98+
Args:
99+
left: A sorted list of integers.
100+
right: A sorted list of integers.
127101
128-
def merge_iter(input_list: list[int], low: int, mid: int, high: int) -> list[int]:
129-
"""
130-
Sorts the left-half and right-half individually then merges them into a result.
102+
Returns:
103+
A merged and sorted list of integers.
131104
132-
:param input_list: List to be sorted.
133-
:param low: Starting index of the segment.
134-
:param mid: Middle index to split the segment.
135-
:param high: Ending index of the segment.
136-
:return: Merged sorted list.
105+
Example:
106+
>>> merge([1, 3, 5], [2, 4, 6])
107+
[1, 2, 3, 4, 5, 6]
137108
"""
138-
left = input_list[low : mid + 1]
139-
right = input_list[mid + 1 : high + 1]
140-
result = []
141-
109+
merged = []
142110
i = j = 0
111+
143112
while i < len(left) and j < len(right):
144-
if left[i] <= right[j]:
145-
result.append(left[i])
113+
if left[i] < right[j]:
114+
merged.append(left[i])
146115
i += 1
147116
else:
148-
result.append(right[j])
117+
merged.append(right[j])
149118
j += 1
150119

151-
result.extend(left[i:])
152-
result.extend(right[j:])
153-
154-
input_list[low : high + 1] = result
155-
return input_list
156-
157-
158-
def iter_merge_sort(input_list: list[int]) -> list[int]:
159-
"""
160-
Return a sorted copy of the input list using iterative merge sort.
161-
162-
>>> iter_merge_sort([5, 9, 8, 7, 1, 2, 7])
163-
[1, 2, 5, 7, 7, 8, 9]
164-
>>> iter_merge_sort([1])
165-
[1]
166-
>>> iter_merge_sort([2, 1])
167-
[1, 2]
168-
>>> iter_merge_sort([2, 1, 3])
169-
[1, 2, 3]
170-
>>> iter_merge_sort([4, 3, 2, 1])
171-
[1, 2, 3, 4]
172-
>>> iter_merge_sort([5, 4, 3, 2, 1])
173-
[1, 2, 3, 4, 5]
174-
>>> iter_merge_sort(['c', 'b', 'a'])
175-
['a', 'b', 'c']
176-
>>> iter_merge_sort([0.3, 0.2, 0.1])
177-
[0.1, 0.2, 0.3]
178-
>>> iter_merge_sort(['dep', 'dang', 'trai'])
179-
['dang', 'dep', 'trai']
180-
>>> iter_merge_sort([6])
181-
[6]
182-
>>> iter_merge_sort([])
183-
[]
184-
>>> iter_merge_sort([-2, -9, -1, -4])
185-
[-9, -4, -2, -1]
186-
>>> iter_merge_sort([1.1, 1, 0.0, -1, -1.1])
187-
[-1.1, -1, 0.0, 1, 1.1]
188-
"""
189-
if not isinstance(input_list, list):
190-
raise TypeError("Input must be a list")
191-
192-
n = len(input_list)
193-
if n <= 1:
194-
return input_list
120+
merged.extend(left[i:])
121+
merged.extend(right[j:])
195122

196-
width = 1
197-
while width < n:
198-
for i in range(0, n, 2 * width):
199-
low = i
200-
mid = min(i + width - 1, n - 1)
201-
high = min(i + 2 * width - 1, n - 1)
202-
if mid < high:
203-
merge_iter(input_list, low, mid, high)
204-
width *= 2
205-
206-
return input_list
123+
return merged
207124

208-
209-
def merge_sort(collection: list[int]) -> list[int]:
125+
def merge_insertion_sort(arr: List[int]) -> List[int]:
210126
"""
211-
Sorts a list using the merge sort algorithm.
127+
Perform merge insertion sort on a list of integers.
212128
213-
:param collection: A mutable ordered collection with comparable items.
214-
:return: The same collection ordered in ascending order.
129+
Args:
130+
arr: A list of integers.
215131
216-
Time Complexity: O(n log n)
132+
Returns:
133+
A sorted list of integers.
217134
218-
>>> merge_sort([0, 5, 3, 2, 2])
219-
[0, 2, 2, 3, 5]
220-
>>> merge_sort([])
221-
[]
222-
>>> merge_sort([-2, -5, -45])
223-
[-45, -5, -2]
135+
Example:
136+
>>> merge_insertion_sort([8, 3, 5, 6])
137+
[3, 5, 6, 8]
224138
"""
139+
if len(arr) <= 1:
140+
return arr
225141

226-
def merge(left: list[int], right: list[int]) -> list[int]:
227-
"""
228-
Merge two sorted lists into a single sorted list.
229-
230-
:param left: Left collection
231-
:param right: Right collection
232-
:return: Merged result
233-
"""
234-
result = []
235-
while left and right:
236-
result.append(left.pop(0) if left[0] <= right[0] else right.pop(0))
237-
result.extend(left)
238-
result.extend(right)
239-
return result
142+
mid = len(arr) // 2
143+
left = arr[:mid]
144+
right = arr[mid:]
240145

241-
if len(collection) <= 1:
242-
return collection
243-
mid_index = len(collection) // 2
244-
return merge(merge_sort(collection[:mid_index]), merge_sort(collection[mid_index:]))
146+
left = merge_insertion_sort(left)
147+
right = merge_insertion_sort(right)
245148

149+
return merge(left, right)
246150

247151
if __name__ == "__main__":
248-
if not hasattr(sys, "gettrace") or sys.gettrace() is None:
249-
doctest.testmod() # Only run tests if not debugging
250-
251-
try:
252-
user_input = input("Enter numbers separated by a comma:\n").strip()
253-
unsorted = [int(item) for item in user_input.split(",")]
254-
255-
sorted_list_merge_sort = merge_sort(unsorted)
256-
sorted_list_iter_merge_sort = iter_merge_sort(
257-
unsorted.copy()
258-
) # Use copy to avoid sorting the same list twice
259-
sorted_list_merge_insertion_sort = merge_insertion_sort(unsorted.copy())
260-
261-
print(f"Unsorted list: {unsorted}")
262-
print(f"Sorted list using merge_sort: {sorted_list_merge_sort}")
263-
print(f"Sorted list using iter_merge_sort: {sorted_list_iter_merge_sort}")
264-
print(
265-
f"Sorted list using merge_insertion_sort: {sorted_list_merge_insertion_sort}"
266-
)
267-
268-
print("Merge Sort Result:", *sorted_list_merge_sort, sep=",")
269-
print("Iterative Merge Sort Result:", *sorted_list_iter_merge_sort, sep=",")
270-
print(
271-
"Merge Insertion Sort Result:", *sorted_list_merge_insertion_sort, sep=","
272-
)
273-
except ValueError:
274-
print("Invalid input. Please enter valid integers separated by commas.")
152+
import doctest
153+
doctest.testmod()

0 commit comments

Comments
 (0)