Skip to content

Commit da68ba5

Browse files
committed
Adding Fibonacci Heap Sort algorithm
1 parent 3a86316 commit da68ba5

File tree

11 files changed

+129
-16
lines changed

11 files changed

+129
-16
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ $ pip install py-algorithms
3636
- Binary Search
3737

3838
^ Sorting
39-
- Bubble Sort
39+
- Heap Sort (Fibonacci heap)
4040
- Merge Sort
41+
- Bubble Sort
4142
- Selection Sort
4243

4344
^ Agorithms
@@ -117,6 +118,29 @@ xs = [0, 6, 7, 8, 9, 4, 5, 12, -1]
117118
sorting_algorithm = new_selection_sort()
118119
sorting_algorithm(xs)
119120

121+
#=> [-1, 0, 4, 5, 6, 7, 8, 9, 12]
122+
```
123+
#### *Heap Sort (https://en.wikipedia.org/wiki/Heapsort)
124+
125+
In computer science, heapsort is a comparison-based sorting algorithm.
126+
Heapsort can be thought of as an improved selection sort: like that algorithm,
127+
it divides its input into a sorted and an unsorted region, and it iteratively
128+
shrinks the unsorted region by extracting the largest element and moving
129+
that to the sorted region. The improvement consists of the use of a heap data structure
130+
rather than a linear-time search to find the maximum.
131+
132+
Worst case: О(n log n)
133+
Best case: О(n)
134+
Average: О(n log n)
135+
Worst case space: O(1)
136+
137+
```python
138+
from py_algorithms.sort import new_heap_sort
139+
140+
xs = [0, 6, 7, 8, 9, 4, 5, 12, -1]
141+
sorting_algorithm = new_heap_sort()
142+
sorting_algorithm(xs)
143+
120144
#=> [-1, 0, 4, 5, 6, 7, 8, 9, 12]
121145
```
122146
---

py_algorithms/data_structures/heap.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from typing import List
55
from typing import Union
66

7+
from ..utils import test_iterable
8+
79

810
class _HeapNode:
911
def __init__(self, key: Any, value: Any):
@@ -21,7 +23,10 @@ def is_marked(self) -> bool:
2123

2224

2325
class Heap:
26+
MAX_MIN = -sys.maxsize
27+
2428
def __init__(self, comparator_f2: Callable[[Any, Any], bool], xs: List[Any] = ()):
29+
test_iterable(xs)
2530
self._size = 0
2631
self._comparator_f2 = comparator_f2
2732
self._next = None
@@ -39,11 +44,11 @@ def _get_by_index(array, index) -> Union[None, Any]:
3944

4045
@classmethod
4146
def _set_entry_by_index(cls, array, index, value):
42-
if cls._get_by_index(array, index) == -sys.maxsize:
47+
if cls._get_by_index(array, index) == cls.MAX_MIN:
4348
array[index] = value
4449
return array
4550
else:
46-
array.extend([-sys.maxsize] * (index - len(array) + 1))
51+
array.extend([cls.MAX_MIN] * (index - len(array) + 1))
4752
return cls._set_entry_by_index(array, index, value)
4853

4954
@property
@@ -78,7 +83,7 @@ def contains_key(self, key) -> bool:
7883
return False
7984

8085
def push(self, key: Any, value: any) -> Any:
81-
if not key:
86+
if key is None:
8287
raise RuntimeError('Could not process heap keys equal to Null.')
8388

8489
node = _HeapNode(key, value)
@@ -174,7 +179,7 @@ def _consolidate(self):
174179
# we'll try to consolidate them
175180
degree = root.degree
176181

177-
while self._get_by_index(degrees, degree):
182+
while not (self._get_by_index(degrees, degree) in [self.MAX_MIN, None]):
178183
other_root_with_degree = degrees[degree]
179184

180185
if self._comparator_f2(root.key, other_root_with_degree.key):
@@ -185,7 +190,7 @@ def _consolidate(self):
185190
smaller, larger = other_root_with_degree, root
186191

187192
self._link_nodes(larger, smaller)
188-
degrees[degree] = -sys.maxsize
193+
degrees[degree] = self.MAX_MIN
189194
root = smaller
190195
degree += 1
191196

py_algorithms/sort/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import List
33

44
from ._bubble_sort import BubbleSort
5+
from ._heap_sort import FibonacciHeapSort
56
from ._merge_sort import RecursiveMergeSort
67
from ._selection_sort import SelectionSort
78
from .sort import Sort
@@ -57,3 +58,19 @@ def new_selection_sort() -> Callable[[List[int]], List[int]]:
5758
:return: a function1 interface to apply
5859
"""
5960
return lambda xs: SelectionSort().sort(xs)
61+
62+
63+
def new_heap_sort() -> Callable[[List[int]], List[int]]:
64+
"""
65+
- Fibonacci Heap Sort. Factory method to return sort functor.
66+
67+
>>> from py_algorithms.sort import new_heap_sort
68+
>>>
69+
>>> xs = [0, 6, 7, 8, 9, 4, 5, 12, -1]
70+
>>> sorting_algorithm = new_heap_sort()
71+
>>> sorting_algorithm(xs) #=> [-1, 0, 4, 5, 6, 7, 8, 9, 12]
72+
73+
:return: a function1 interface to apply
74+
"""
75+
76+
return lambda xs: FibonacciHeapSort().sort(xs)

py_algorithms/sort/_bubble_sort.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import List
22

3+
from ..utils import test_iterable
34
from ..utils import three_way_cmp
45
from .sort import Sort
56

@@ -15,7 +16,7 @@ class BubbleSort(Sort):
1516
"""
1617

1718
def sort(self, xs: List[int]) -> List[int]:
18-
self._test_iterable(xs)
19+
test_iterable(xs)
1920

2021
done = False
2122
while not done:

py_algorithms/sort/_heap_sort.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import List
2+
3+
from ..data_structures import new_min_heap
4+
from ..utils import test_iterable
5+
from .sort import Sort
6+
7+
8+
class FibonacciHeapSort(Sort):
9+
"""
10+
- Fibonacci Heap Sort. Factory method to return sort functor.
11+
12+
>>> from py_algorithms.sort import new_heap_sort
13+
>>>
14+
>>> xs = [0, 6, 7, 8, 9, 4, 5, 12, -1]
15+
>>> sorting_algorithm = new_heap_sort()
16+
>>> sorting_algorithm(xs) #=> [-1, 0, 4, 5, 6, 7, 8, 9, 12]
17+
18+
Worst case: О(n log n)
19+
Best case: О(n)
20+
Average: О(n log n)
21+
Worst case space: O(1)
22+
"""
23+
24+
def sort(self, xs: List[int]) -> List[int]:
25+
test_iterable(xs)
26+
27+
heap = new_min_heap(xs)
28+
xs = []
29+
while not heap.is_empty:
30+
xs.append(heap.pop())
31+
return xs
32+
33+
34+
class HeapSort(FibonacciHeapSort):
35+
pass

py_algorithms/sort/_merge_sort.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import List
22

3+
from ..utils import test_iterable
34
from .sort import Sort
45

56

@@ -13,7 +14,7 @@ class RecursiveMergeSort(Sort):
1314
"""
1415

1516
def sort(self, xs: List[int]) -> List[int]:
16-
self._test_iterable(xs)
17+
test_iterable(xs)
1718
if len(xs) <= 1:
1819
return xs
1920

py_algorithms/sort/_selection_sort.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import List
22

3+
from ..utils import test_iterable
34
from ..utils import three_way_cmp
45
from .sort import Sort
56

@@ -12,7 +13,7 @@ class SelectionSort(Sort):
1213
"""
1314

1415
def sort(self, xs: List[int]) -> List[int]:
15-
self._test_iterable(xs)
16+
test_iterable(xs)
1617

1718
for i in range(0, len(xs)):
1819
min_val = i

py_algorithms/sort/sort.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
import abc
2-
from typing import Any
32
from typing import List
43

54

65
class Sort(metaclass=abc.ABCMeta):
76
@abc.abstractmethod
87
def sort(self, xs: List[int]) -> List[int]:
98
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.')

py_algorithms/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ def three_way_cmp(a: Any, b: Any) -> int:
77
single operation, in accordance with the mathematical law of trichotomy.
88
"""
99
return (a > b) - (a < b)
10+
11+
12+
def test_iterable(xs: Any):
13+
if not isinstance(xs, (list, tuple)):
14+
raise RuntimeError('Can sort only iterable entity.')

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
'heap', 'fibonacci heap', 'max heap', 'min heap', 'priority queue',
2323
'fibonacci priority queue', 'max priority queue', 'min priority queue',
2424
'dynamic connectivity', 'union find', 'quick union', 'weighted quick union',
25-
'weighted quick union with path compression'],
25+
'weighted quick union with path compression', 'fibonacci heap sort', 'heapsort',
26+
'heap sort'],
2627
description="Library of Algorithms, Data Structures, variety of solutions to common "
2728
"CS problems",
2829
long_description="Algorithms and Data Structures implemented using awesome Python.",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import Callable
2+
from typing import List
3+
4+
import pytest
5+
6+
from py_algorithms.sort import new_heap_sort
7+
from tests.conftest import large_xs
8+
from tests.conftest import xs
9+
10+
11+
@pytest.fixture
12+
def new_sorting_algorithm() -> Callable[[List[int]], List[int]]:
13+
return new_heap_sort()
14+
15+
16+
class TestHeapSort:
17+
def test_sorting_algorithm(self):
18+
f = new_sorting_algorithm()
19+
assert sorted(xs()) == f(xs())
20+
21+
def test_sorting_algorithm_on_large_xs(self):
22+
f = new_sorting_algorithm()
23+
assert sorted(large_xs()) == f(large_xs())
24+
25+
def test_non_iterable_xs(self):
26+
f = new_sorting_algorithm()
27+
for x in (None, 1, ''):
28+
with pytest.raises(RuntimeError):
29+
f(x)

0 commit comments

Comments
 (0)