Skip to content

Commit f0aa63f

Browse files
authored
Add randomized heap. (#3241)
1 parent 35eefac commit f0aa63f

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

Diff for: data_structures/heap/randomized_heap.py

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import random
6+
from typing import Generic, Iterable, List, Optional, TypeVar
7+
8+
T = TypeVar("T")
9+
10+
11+
class RandomizedHeapNode(Generic[T]):
12+
"""
13+
One node of the randomized heap. Contains the value and references to
14+
two children.
15+
"""
16+
17+
def __init__(self, value: T) -> None:
18+
self._value: T = value
19+
self.left: Optional[RandomizedHeapNode[T]] = None
20+
self.right: Optional[RandomizedHeapNode[T]] = None
21+
22+
@property
23+
def value(self) -> T:
24+
"""Return the value of the node."""
25+
return self._value
26+
27+
@staticmethod
28+
def merge(
29+
root1: Optional[RandomizedHeapNode[T]], root2: Optional[RandomizedHeapNode[T]]
30+
) -> Optional[RandomizedHeapNode[T]]:
31+
"""Merge 2 nodes together."""
32+
if not root1:
33+
return root2
34+
35+
if not root2:
36+
return root1
37+
38+
if root1.value > root2.value:
39+
root1, root2 = root2, root1
40+
41+
if random.choice([True, False]):
42+
root1.left, root1.right = root1.right, root1.left
43+
44+
root1.left = RandomizedHeapNode.merge(root1.left, root2)
45+
46+
return root1
47+
48+
49+
class RandomizedHeap(Generic[T]):
50+
"""
51+
A data structure that allows inserting a new value and to pop the smallest
52+
values. Both operations take O(logN) time where N is the size of the
53+
structure.
54+
Wiki: https://en.wikipedia.org/wiki/Randomized_meldable_heap
55+
56+
>>> RandomizedHeap([2, 3, 1, 5, 1, 7]).to_sorted_list()
57+
[1, 1, 2, 3, 5, 7]
58+
59+
>>> rh = RandomizedHeap()
60+
>>> rh.pop()
61+
Traceback (most recent call last):
62+
...
63+
IndexError: Can't get top element for the empty heap.
64+
65+
>>> rh.insert(1)
66+
>>> rh.insert(-1)
67+
>>> rh.insert(0)
68+
>>> rh.to_sorted_list()
69+
[-1, 0, 1]
70+
"""
71+
72+
def __init__(self, data: Optional[Iterable[T]] = ()) -> None:
73+
"""
74+
>>> rh = RandomizedHeap([3, 1, 3, 7])
75+
>>> rh.to_sorted_list()
76+
[1, 3, 3, 7]
77+
"""
78+
self._root: Optional[RandomizedHeapNode[T]] = None
79+
for item in data:
80+
self.insert(item)
81+
82+
def insert(self, value: T) -> None:
83+
"""
84+
Insert the value into the heap.
85+
86+
>>> rh = RandomizedHeap()
87+
>>> rh.insert(3)
88+
>>> rh.insert(1)
89+
>>> rh.insert(3)
90+
>>> rh.insert(7)
91+
>>> rh.to_sorted_list()
92+
[1, 3, 3, 7]
93+
"""
94+
self._root = RandomizedHeapNode.merge(self._root, RandomizedHeapNode(value))
95+
96+
def pop(self) -> T:
97+
"""
98+
Pop the smallest value from the heap and return it.
99+
100+
>>> rh = RandomizedHeap([3, 1, 3, 7])
101+
>>> rh.pop()
102+
1
103+
>>> rh.pop()
104+
3
105+
>>> rh.pop()
106+
3
107+
>>> rh.pop()
108+
7
109+
>>> rh.pop()
110+
Traceback (most recent call last):
111+
...
112+
IndexError: Can't get top element for the empty heap.
113+
"""
114+
result = self.top()
115+
self._root = RandomizedHeapNode.merge(self._root.left, self._root.right)
116+
117+
return result
118+
119+
def top(self) -> T:
120+
"""
121+
Return the smallest value from the heap.
122+
123+
>>> rh = RandomizedHeap()
124+
>>> rh.insert(3)
125+
>>> rh.top()
126+
3
127+
>>> rh.insert(1)
128+
>>> rh.top()
129+
1
130+
>>> rh.insert(3)
131+
>>> rh.top()
132+
1
133+
>>> rh.insert(7)
134+
>>> rh.top()
135+
1
136+
"""
137+
if not self._root:
138+
raise IndexError("Can't get top element for the empty heap.")
139+
return self._root.value
140+
141+
def clear(self):
142+
"""
143+
Clear the heap.
144+
145+
>>> rh = RandomizedHeap([3, 1, 3, 7])
146+
>>> rh.clear()
147+
>>> rh.pop()
148+
Traceback (most recent call last):
149+
...
150+
IndexError: Can't get top element for the empty heap.
151+
"""
152+
self._root = None
153+
154+
def to_sorted_list(self) -> List[T]:
155+
"""
156+
Returns sorted list containing all the values in the heap.
157+
158+
>>> rh = RandomizedHeap([3, 1, 3, 7])
159+
>>> rh.to_sorted_list()
160+
[1, 3, 3, 7]
161+
"""
162+
result = []
163+
while self:
164+
result.append(self.pop())
165+
166+
return result
167+
168+
def __bool__(self) -> bool:
169+
"""
170+
Check if the heap is not empty.
171+
172+
>>> rh = RandomizedHeap()
173+
>>> bool(rh)
174+
False
175+
>>> rh.insert(1)
176+
>>> bool(rh)
177+
True
178+
>>> rh.clear()
179+
>>> bool(rh)
180+
False
181+
"""
182+
return self._root is not None
183+
184+
185+
if __name__ == "__main__":
186+
import doctest
187+
188+
doctest.testmod()

0 commit comments

Comments
 (0)