-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhollow_heap.py
185 lines (155 loc) · 4.81 KB
/
hollow_heap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import math
import abstract_heap as heap
class _Node(heap.HeapNode):
def __init__(self, key, val):
self.key = key
self.item = _Item(val, self)
self.child = self.right = None
self.ep = None # extra parent, only hollow can have it
self.rank = 0
@property
def val(self):
if self.item is not None:
return self.item.val
else:
return None
@val.setter
def val(self, val):
if self.item is not None:
self.item.val = val
class _Item:
def __init__(self, val, node):
self.val = val
self.node = node
# Implementation of Hollow Heap
# https://arxiv.org/abs/1510.06535
class HollowHeap(heap.Heap):
def __init__(self):
self.min = None
self.no_nodes = 0
# Return the minimum node.
# Amortized time complexity: O(1)
def find_min(self):
return self.min
# Insert new item as a node to the heap.
# Can be called with key (key) or value and key (key, value).
# Return the node.
# Amortized time complexity: O(1)
def insert(self, key, value=None):
if value is None:
value = key
n = _Node(key, value)
self.min = self._meld(n, self.min)
self.no_nodes += 1
return n
# Delete and returns the minimum node.
# Amortized time complexity: O(log n)
def delete_min(self):
prev_min = self.min
self.delete(self.min)
return prev_min
# Delete the given node.
# Amortized time complexity: O(log n)
def delete(self, node):
if node is None:
return
node.item = None
node = None
# lazy deletion
if self.min.item is not None:
self.no_nodes -= 1
return self.min
A = {}
h = self.min # use same naming as in pseudo code
h.right = None
while h is not None:
w = h.child
v = h
h = h.right
# loop through descendants
while w is not None:
u = w
w = w.right
# if hollow
if u.item is None:
if u.ep is None:
u.right = h
h = u
else:
if u.ep == v:
w = None
else:
u.right = None
u.ep = None
else:
# do ranked links
# similiar to fibonacci heap, unique ranks
while u.rank in A:
u = self._link(u, A[u.rank])
del A[u.rank]
u.rank += 1
A[u.rank] = u
# do unranked links
# combine subtrees with unique rank and finds the root, aka min
for i in A:
if h is None:
h = A[i]
else:
h = self._link(h, A[i]) # returns the smaller one
self.no_nodes -= 1
# update the min
self.min = h
if self.min is not None:
self.min.right = None
# Decrease the value of the key of the given nodes.
# new_key must lower than current key value.
# Return the updated node.
# Amortized time complexity: O(1)
def decrease_key(self, node, new_key):
assert (
node.key > new_key
), "The new_key must be lower than current when decreasing key."
assert (
node.item is not None
), "The node is missing item. It's hollow. Cannot be decreased."
u = node # same naming as in pseudo code
# decrease min value, simple case
if u == self.min:
u.key = new_key
return self.min
# otherwise
v = _Node(new_key, u.item.val)
u.item = None
if u.rank > 2:
v.rank = u.rank - 2
v.child = u
u.ep = v
h = self._link(v, self.min)
if h != self.min:
self.min = h
return v
# Merge another heap into this heap
# Amortized time complexity: O(1)
def merge(self, heap):
assert isinstance(heap, HollowHeap)
self.min = self._meld(self.min, heap.min)
self.no_nodes += heap.no_nodes
# Combine two sub trees
def _meld(self, n, m):
if n is None:
return m
if m is None:
return n
return self._link(n, m)
# Make given node to be a child of another given node
def _add_child(self, child, parent):
child.right = parent.child
parent.child = child
# Links two nodes together
def _link(self, n, m):
if m.key > n.key:
self._add_child(m, n)
return n
else:
self._add_child(n, m)
return m