Skip to content

Commit 943afbb

Browse files
authored
Merge pull request #8 from rlishtaba/feature/merge-sort
Adding Merge Sort algorithm
2 parents f1b7f54 + 233b47e commit 943afbb

File tree

6 files changed

+113
-9
lines changed

6 files changed

+113
-9
lines changed

py_algorithms/sort/__init__.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,41 @@
22
from typing import List
33

44
from ._bubble_sort import BubbleSort
5+
from ._merge_sort import RecursiveMergeSort
56
from .sort import Sort
67

78
__all__ = [
89
'new_bubble_sort',
10+
'new_merge_sort',
911
'Sort'
1012
]
1113

1214

1315
def new_bubble_sort() -> Callable[[List[int]], List[int]]:
1416
"""
15-
Factory method to return function to sort
17+
Factory method to return sort function
18+
19+
>>> from py_algorithms.sort import new_merge_sort
20+
>>>
21+
>>> xs = [0, 6, 7, 8, 9, 4, 5, 12, -112]
22+
>>> sorting_algorithm = new_bubble_sort()
23+
>>> sorting_algorithm(xs) #=> [-112, 0, 4, 5, 6, 7, 8, 9, 12]
24+
25+
:return: a function1 interface to apply
26+
"""
27+
return lambda xs: BubbleSort().sort(xs)
28+
29+
30+
def new_merge_sort() -> Callable[[List[int]], List[int]]:
31+
"""
32+
Factory method to return sort function
33+
34+
>>> from py_algorithms.sort import new_merge_sort
35+
>>>
36+
>>> xs = [0, 6, 7, 8, 9, 4, 5, 12, -1]
37+
>>> sorting_algorithm = new_merge_sort()
38+
>>> sorting_algorithm(xs) #=> [-1, 0, 4, 5, 6, 7, 8, 9, 12]
39+
1640
:return: a function1 interface to apply
1741
"""
18-
return lambda xs: BubbleSort().sort(xs)
42+
return lambda xs: RecursiveMergeSort().sort(xs)

py_algorithms/sort/_bubble_sort.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ class BubbleSort(Sort):
1515
"""
1616

1717
def sort(self, xs: List[int]) -> List[int]:
18-
if not isinstance(xs, (list, tuple)):
19-
raise RuntimeError('Can sort only iterable entity.')
18+
self._test_iterable(xs)
2019

2120
done = False
2221
while not done:

py_algorithms/sort/_merge_sort.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import List
2+
3+
from .sort import Sort
4+
5+
6+
class RecursiveMergeSort(Sort):
7+
"""
8+
Time: О(N log N)
9+
Space: О(N)
10+
Stability: true
11+
12+
Recursive implementation
13+
"""
14+
15+
def sort(self, xs: List[int]) -> List[int]:
16+
self._test_iterable(xs)
17+
if len(xs) <= 1:
18+
return xs
19+
20+
mid = len(xs) // 2
21+
left = xs[:mid]
22+
right = xs[mid:]
23+
24+
return self.merge(self.sort(left), self.sort(right))
25+
26+
@staticmethod
27+
def merge(left: List[int], right: List[int]) -> List[int]:
28+
xs = []
29+
while not (len(left) == 0 or len(right) == 0):
30+
if left[0] <= right[0]:
31+
xs.append(left.pop(0))
32+
else:
33+
xs.append(right.pop(0))
34+
return xs + left + right

py_algorithms/sort/sort.py

+6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import abc
2+
from typing import Any
23
from typing import List
34

45

56
class Sort(metaclass=abc.ABCMeta):
67
@abc.abstractmethod
78
def sort(self, xs: List[int]) -> List[int]:
89
raise NotImplemented()
10+
11+
@staticmethod
12+
def _test_iterable(xs: Any):
13+
if not isinstance(xs, (list, tuple)):
14+
raise RuntimeError('Can sort only iterable entity.')

tests/py_algorithms/sort/bubble_sort_test.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import copy
2+
import random
23
from typing import List
34

45
import pytest
@@ -8,18 +9,25 @@
89

910
@pytest.fixture
1011
def xs() -> List[int]:
11-
return [0, 6, 7, 8, 9, 4, 5, 12]
12+
return [0, 6, 7, 8, 9, 4, 5, 12, -112]
1213

1314

14-
@pytest.fixture
15-
def sorted_xs() -> List[int]:
16-
return sorted(xs())
15+
def large_xs() -> List[int]:
16+
raw = [0, 6, 7, 8, 9, 4, 5, 12, -112] * 50
17+
random.shuffle(raw)
18+
return raw
1719

1820

1921
class TestBubbleSort:
2022
def test_sorting_algorithm(self):
2123
f = new_bubble_sort()
22-
assert sorted_xs() == f(xs())
24+
assert sorted(xs()) == f(xs())
25+
decreasing_xs = [999, 10, 9, 8, 7, 6]
26+
assert sorted(copy.copy(decreasing_xs)) == f(decreasing_xs)
27+
28+
def test_sorting_algorithm_on_large_set(self):
29+
f = new_bubble_sort()
30+
assert sorted(large_xs()) == f(large_xs())
2331
decreasing_xs = [999, 10, 9, 8, 7, 6]
2432
assert sorted(copy.copy(decreasing_xs)) == f(decreasing_xs)
2533

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import random
2+
from typing import List
3+
4+
import pytest
5+
6+
from py_algorithms.sort import new_merge_sort
7+
8+
9+
@pytest.fixture
10+
def xs() -> List[int]:
11+
return [0, 6, 7, 8, 9, 4, 5, 12, -1]
12+
13+
14+
def large_xs() -> List[int]:
15+
raw = [0, 6, 7, 8, 9, 4, 5, 12, -112] * 50
16+
random.shuffle(raw)
17+
return raw
18+
19+
20+
class TestMergeSort:
21+
def test_sorting_algorithm(self):
22+
f = new_merge_sort()
23+
assert sorted(xs()) == f(xs())
24+
25+
def test_sorting_algorithm_on_large_xs(self):
26+
f = new_merge_sort()
27+
assert sorted(large_xs()) == f(large_xs())
28+
29+
def test_non_iterable_xs(self):
30+
f = new_merge_sort()
31+
for x in (None, 1, ''):
32+
with pytest.raises(RuntimeError):
33+
f(x)

0 commit comments

Comments
 (0)