|
8 | 8 | For manual testing run: |
9 | 9 | python merge_sort.py |
10 | 10 | """ |
| 11 | +from typing import Protocol, TypeVar |
11 | 12 |
|
| 13 | +# CHANGED: Added Comparable Protocol. |
| 14 | +# WHY: Merge sort is a comparison-based sorting algorithm, so it should |
| 15 | +# support any type of item that can be compared using the < operator, |
| 16 | +# not only integers. |
12 | 17 |
|
13 | | -def merge_sort(collection: list) -> list: |
| 18 | +class Comparable(Protocol): |
| 19 | + def __lt__(self, other: object, /) -> bool: ... |
| 20 | + |
| 21 | +# CHANGED: Added a TypeVar bounded to Comparable. |
| 22 | +# WHY: This preserves the input element type while ensuring that the |
| 23 | +# elements support comparison. |
| 24 | + |
| 25 | + |
| 26 | +T = TypeVar("T", bound=Comparable) |
| 27 | + |
| 28 | +# CHANGED: list[int] -> list[T] |
| 29 | +# WHY: Merge sort can sort any comparable items such as ints, strings, |
| 30 | +# and floats. |
| 31 | + |
| 32 | + |
| 33 | +def merge_sort(collection: list[T]) -> list[T]: |
14 | 34 | """ |
15 | 35 | Sorts a list using the merge sort algorithm. |
16 | 36 |
|
17 | | - :param collection: A mutable ordered collection with comparable items. |
18 | | - :return: The same collection ordered in ascending order. |
| 37 | + :param collection: A collection with comparable items. |
| 38 | + :return: The collection ordered in ascending order. |
19 | 39 |
|
20 | 40 | Time Complexity: O(n log n) |
21 | 41 | Space Complexity: O(n) |
22 | 42 |
|
23 | 43 | Examples: |
24 | 44 | >>> merge_sort([0, 5, 3, 2, 2]) |
25 | 45 | [0, 2, 2, 3, 5] |
| 46 | +
|
26 | 47 | >>> merge_sort([]) |
27 | 48 | [] |
28 | | - >>> merge_sort([-2, -5, -45]) |
| 49 | +
|
| 50 | + >>> merge_sort([-2, -45, -5]) |
29 | 51 | [-45, -5, -2] |
| 52 | +
|
| 53 | + # CHANGED: Added a string example. |
| 54 | + # WHY: Proves merge_sort works with comparable non-integer types. |
| 55 | + >>> merge_sort(["c", "a", "b"]) |
| 56 | + ['a', 'b', 'c'] |
| 57 | +
|
| 58 | + # CHANGED: Added a float example. |
| 59 | + # WHY: Further proves the algorithm is not restricted to integers. |
| 60 | + >>> merge_sort([2.5, -1.0, 0.0]) |
| 61 | + [-1.0, 0.0, 2.5] |
30 | 62 | """ |
31 | 63 |
|
32 | | - def merge(left: list, right: list) -> list: |
| 64 | + def merge(left: list[T], right: list[T]) -> list[T]: |
33 | 65 | """ |
34 | 66 | Merge two sorted lists into a single sorted list. |
35 | 67 |
|
36 | 68 | :param left: Left collection |
37 | 69 | :param right: Right collection |
38 | 70 | :return: Merged result |
39 | 71 | """ |
40 | | - result = [] |
| 72 | + result: list[T] = [] |
41 | 73 | while left and right: |
42 | | - result.append(left.pop(0) if left[0] <= right[0] else right.pop(0)) |
| 74 | + # CHANGED: Use only < instead of <=. |
| 75 | + # WHY: Comparable guarantees the < operator. Requiring <= |
| 76 | + # would unnecessarily require comparable objects to implement |
| 77 | + # an additional comparison method. |
| 78 | + if right[0] < left[0]: |
| 79 | + result.append(right.pop(0)) |
| 80 | + else: |
| 81 | + result.append(left.pop(0)) |
43 | 82 | result.extend(left) |
44 | 83 | result.extend(right) |
45 | 84 | return result |
|
0 commit comments