1
1
"""
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
10
9
"""
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.
11
18
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
12
36
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 ]:
14
202
"""
15
203
Sorts a list using the merge sort algorithm.
16
204
@@ -19,16 +207,14 @@ def merge_sort(collection: list) -> list:
19
207
20
208
Time Complexity: O(n log n)
21
209
22
- Examples:
23
210
>>> merge_sort([0, 5, 3, 2, 2])
24
211
[0, 2, 2, 3, 5]
25
212
>>> merge_sort([])
26
213
[]
27
214
>>> merge_sort([-2, -5, -45])
28
215
[-45, -5, -2]
29
216
"""
30
-
31
- def merge (left : list , right : list ) -> list :
217
+ def merge (left : list [int ], right : list [int ]) -> list [int ]:
32
218
"""
33
219
Merge two sorted lists into a single sorted list.
34
220
@@ -48,16 +234,27 @@ def merge(left: list, right: list) -> list:
48
234
mid_index = len (collection ) // 2
49
235
return merge (merge_sort (collection [:mid_index ]), merge_sort (collection [mid_index :]))
50
236
51
-
52
237
if __name__ == "__main__" :
53
- import doctest
54
238
55
- doctest .testmod ()
239
+ if not hasattr (sys , 'gettrace' ) or sys .gettrace () is None :
240
+ doctest .testmod () # Only run tests if not debugging
56
241
57
242
try :
58
243
user_input = input ("Enter numbers separated by a comma:\n " ).strip ()
59
244
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 = "," )
62
258
except ValueError :
63
259
print ("Invalid input. Please enter valid integers separated by commas." )
260
+
0 commit comments