Skip to content

Commit 35148b8

Browse files
committed
Add skew heap data structure.
1 parent 695217e commit 35148b8

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

Diff for: data_structures/heap/skew_heap.py

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
from typing import TypeVar, Generic, Optional, Iterable, List
5+
6+
__all__ = ["SkewHeap"]
7+
8+
T = TypeVar("T")
9+
10+
11+
class SkewNode(Generic[T]):
12+
"""One node of the skew heap. Contains the value and references to two children."""
13+
14+
def __init__(self, value: T) -> None:
15+
self._value: T = value
16+
self.left: Optional[SkewNode[T]] = None
17+
self.right: Optional[SkewNode[T]] = None
18+
19+
@property
20+
def value(self) -> T:
21+
"""Return the value of the node."""
22+
return self._value
23+
24+
@staticmethod
25+
def merge(
26+
root1: Optional[SkewNode[T]], root2: Optional[SkewNode[T]]
27+
) -> Optional[SkewNode[T]]:
28+
"""Merge 2 nodes together."""
29+
if not root1:
30+
return root2
31+
32+
if not root2:
33+
return root1
34+
35+
if root1.value > root2.value:
36+
root1, root2 = root2, root1
37+
38+
result = root1
39+
temp = root1.right
40+
result.right = root1.left
41+
result.left = SkewNode.merge(temp, root2)
42+
43+
return result
44+
45+
46+
class SkewHeap(Generic[T]):
47+
"""
48+
Data structure that allows to insert a new value and to pop the smallest
49+
values. Both operations take O(logN) time where N is the size of the structure.
50+
- Wiki: https://en.wikipedia.org/wiki/Skew_heap
51+
- Visualisation: https://www.cs.usfca.edu/~galles/visualization/SkewHeap.html
52+
53+
>>> SkewHeap.from_list([2, 3, 1, 5, 1, 7]).to_sorted_list()
54+
[1, 1, 2, 3, 5, 7]
55+
56+
>>> sh = SkewHeap()
57+
>>> sh.insert(1)
58+
>>> sh.top()
59+
1
60+
>>> sh.insert(0)
61+
>>> sh.pop()
62+
0
63+
>>> sh.pop()
64+
1
65+
>>> sh.top()
66+
Traceback (most recent call last):
67+
...
68+
AttributeError: Can't get top element for the empty heap.
69+
"""
70+
71+
def __init__(self) -> None:
72+
self._root: Optional[SkewNode[T]] = None
73+
74+
def insert(self, value: T) -> None:
75+
"""Insert the value into the heap."""
76+
self._root = SkewNode.merge(self._root, SkewNode(value))
77+
78+
def pop(self) -> T:
79+
"""Pop the smallest value from the heap and return it."""
80+
result = self.top()
81+
self._root = SkewNode.merge(self._root.left, self._root.right)
82+
83+
return result
84+
85+
def top(self) -> T:
86+
"""Return the smallest value from the heap."""
87+
if not self._root:
88+
raise AttributeError("Can't get top element for the empty heap.")
89+
return self._root.value
90+
91+
def clear(self):
92+
self._root = None
93+
94+
@staticmethod
95+
def from_list(data: Iterable[T]) -> SkewHeap[T]:
96+
"""Get the sorted list from the heap. Heap will be cleared afterwards."""
97+
result = SkewHeap()
98+
for item in data:
99+
result.insert(item)
100+
101+
return result
102+
103+
def to_sorted_list(self) -> List[T]:
104+
"""Returns sorted list containing all the values in the heap."""
105+
result = []
106+
while self:
107+
result.append(self.pop())
108+
109+
return result
110+
111+
def __bool__(self) -> bool:
112+
"""Check if the heap is not empty."""
113+
return self._root is not None
114+
115+
116+
if __name__ == "__main__":
117+
import doctest
118+
119+
doctest.testmod()

0 commit comments

Comments
 (0)