Skip to content

Commit 45df328

Browse files
committed
Refactor: improve merge_sort implementation for performance and type safety
1 parent cbf39ae commit 45df328

File tree

1 file changed

+54
-39
lines changed

1 file changed

+54
-39
lines changed

sorts/merge_sort.py

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,79 @@
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+
Optimized pure Python implementation of the merge sort algorithm.
3+
4+
Merge Sort is a divide-and-conquer algorithm that splits the input list into halves,
5+
recursively sorts them, and merges the sorted halves.
6+
7+
Source: https://en.wikipedia.org/wiki/Merge_sort
108
"""
119

10+
from __future__ import annotations
1211

13-
def merge_sort(collection: list) -> list:
14-
"""
15-
Sorts a list using the merge sort algorithm.
12+
from collections.abc import Sequence
13+
from typing import Any, Protocol, TypeVar
14+
15+
16+
class Comparable(Protocol):
17+
"""Defines minimal comparison operations required for sorting."""
18+
def __lt__(self, other: Any) -> bool: ...
19+
def __le__(self, other: Any) -> bool: ...
1620

17-
:param collection: A mutable ordered collection with comparable items.
18-
:return: The same collection ordered in ascending order.
1921

20-
Time Complexity: O(n log n)
21-
Space Complexity: O(n)
22+
T = TypeVar("T", bound=Comparable)
23+
24+
25+
def merge_sort(arr: Sequence[T]) -> list[T]: # noqa: UP047
26+
"""
27+
Sort a sequence in ascending order using merge sort.
28+
29+
:param arr: Any sequence of comparable items.
30+
:return: A new sorted list.
2231
23-
Examples:
2432
>>> merge_sort([0, 5, 3, 2, 2])
2533
[0, 2, 2, 3, 5]
2634
>>> merge_sort([])
2735
[]
2836
>>> merge_sort([-2, -5, -45])
2937
[-45, -5, -2]
38+
>>> merge_sort(["b", "a", "c"])
39+
['a', 'b', 'c']
40+
>>> merge_sort((3, 1, 2))
41+
[1, 2, 3]
3042
"""
43+
n = len(arr)
44+
if n <= 1:
45+
return list(arr)
3146

32-
def merge(left: list, right: list) -> list:
33-
"""
34-
Merge two sorted lists into a single sorted list.
47+
mid = n // 2
48+
left = merge_sort(arr[:mid])
49+
right = merge_sort(arr[mid:])
50+
return _merge(left, right)
3551

36-
:param left: Left collection
37-
:param right: Right collection
38-
:return: Merged result
39-
"""
40-
result = []
41-
while left and right:
42-
result.append(left.pop(0) if left[0] <= right[0] else right.pop(0))
43-
result.extend(left)
44-
result.extend(right)
45-
return result
4652

47-
if len(collection) <= 1:
48-
return collection
49-
mid_index = len(collection) // 2
50-
return merge(merge_sort(collection[:mid_index]), merge_sort(collection[mid_index:]))
53+
def _merge(left: list[T], right: list[T]) -> list[T]:
54+
"""Merge two sorted lists efficiently using index pointers."""
55+
merged: list[T] = []
56+
i = j = 0
57+
while i < len(left) and j < len(right):
58+
if left[i] <= right[j]:
59+
merged.append(left[i])
60+
i += 1
61+
else:
62+
merged.append(right[j])
63+
j += 1
64+
merged.extend(left[i:])
65+
merged.extend(right[j:])
66+
return merged
5167

5268

5369
if __name__ == "__main__":
5470
import doctest
5571

56-
doctest.testmod()
72+
doctest.testmod(verbose=True)
5773

5874
try:
59-
user_input = input("Enter numbers separated by a comma:\n").strip()
60-
unsorted = [int(item) for item in user_input.split(",")]
61-
sorted_list = merge_sort(unsorted)
62-
print(*sorted_list, sep=",")
75+
user_input = input("Enter numbers separated by commas:\n").strip()
76+
numbers = [int(x) for x in user_input.split(",") if x.strip()]
77+
print("Sorted:", merge_sort(numbers))
6378
except ValueError:
64-
print("Invalid input. Please enter valid integers separated by commas.")
79+
print("Invalid input. Please enter only comma-separated integers.")

0 commit comments

Comments
 (0)