forked from TheAlgorithms/Python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfibonacci_heap.py
250 lines (219 loc) · 7.13 KB
/
fibonacci_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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
from math import log
class FibonacciNode:
"""
Node in a Fibonacci Heap, containing:
- value
- degree (number of children)
- child (a child node)
- left and right pointers to neighboring nodes
- parent (pointer to the parent node)
- mark (boolean flag to indicate if the node has lost a child)
"""
def __init__(self, val: int) -> None:
"""
Initialize a FibonacciNode with the given value.
"""
self.val = val
self.degree = 0
self.child = None
self.left = self
self.right = self
self.parent = None
self.mark = False
def add_child(self, child):
"""
Add a child to this node.
>>> parent = FibonacciNode(5)
>>> child1 = FibonacciNode(3)
>>> child2 = FibonacciNode(7)
>>> parent.add_child(child1)
>>> parent.child
<__main__.FibonacciNode object at ...>
>>> parent.child.val
3
>>> parent.child.right.val
3
>>> parent.child.left.val
3
>>> parent.child.parent
<__main__.FibonacciNode object at ...>
>>> parent.child.degree
1
>>> parent.add_child(child2)
>>> parent.child.right.val
7
>>> parent.child.left.val
3
>>> child1.parent
<__main__.FibonacciNode object at ...>
>>> child1.left.val
7
>>> child1.right.val
7
>>> child1.degree
0
"""
if self.child is None:
self.child = child
else:
child.right = self.child
child.left = self.child.left
self.child.left.right = child
self.child.left = child
child.parent = self
self.degree += 1
def remove_child(self, child: "FibonacciNode") -> None:
"""
Remove a child from this node's children.
"""
if child == self.child:
if child.right == child:
self.child = None
else:
self.child = child.right
child.left.right = child.right
child.right.left = child.left
child.parent = None
child.mark = False
self.degree -= 1
class FibonacciHeap:
"""
Min-oriented priority queue implemented with the Fibonacci Heap data structure.
It supports:
- Insert element: O(1)
- Find minimum: O(1)
- Merge (meld) heaps: O(1)
- Delete minimum: Amortized O(log n)
- Decrease key: Amortized O(1)
- Delete node: Amortized O(log n)
For more details, refer to the Wikipedia page on [Fibonacci Heap](https://en.wikipedia.org/wiki/Fibonacci_heap).
"""
def __init__(self) -> None:
"""
Initialize an empty Fibonacci Heap.
"""
self.min_node = None
self.root_list = []
self.size = 0
def insert(self, val: int) -> None:
"""
Insert a new element with the given value into the Fibonacci Heap.
"""
new_node = FibonacciNode(val)
if self.min_node is None:
self.min_node = new_node
else:
self._link(self.min_node, new_node)
if val < self.min_node.val:
self.min_node = new_node
self.root_list.append(new_node)
self.size += 1
def _link(self, min_node: FibonacciNode, new_node: FibonacciNode) -> None:
"""
Link two nodes in the Fibonacci Heap.
"""
min_node.add_child(new_node)
def find_min(self) -> int:
"""
Find the minimum element in the Fibonacci Heap.
"""
if self.min_node is None:
raise Exception("Heap is empty")
return self.min_node.val
def _consolidate(self) -> None:
"""
Consolidate nodes with the same degree in the Fibonacci Heap.
"""
max_degree = int(2 * log(self.size) / log(1.618))
degree_table = [None] * (max_degree + 1)
current = self.root_list[:]
for node in current:
degree = node.degree
while degree_table[degree]:
other = degree_table[degree]
if node.val > other.val:
node, other = other, node
self._link(other, node)
degree_table[degree] = None
degree += 1
degree_table[degree] = node
self.min_node = None
for node in degree_table:
if node and (self.min_node is None or node.val < self.min_node.val):
self.min_node = node
def delete_min(self) -> int:
"""
Delete the minimum element from the Fibonacci Heap.
"""
if self.min_node is None:
return None
min_val = self.min_node.val
if self.min_node.child:
children = self.min_node.child
while True:
next_child = children.right
children.parent = None
self.root_list.append(children)
if next_child == self.min_node.child:
break
children = next_child
self.root_list.remove(self.min_node)
self._consolidate()
self._update_min_node()
self.size -= 1
return min_val
def decrease_key(self, node: FibonacciNode, new_val: int) -> None:
"""
Decrease the key of a node in the Fibonacci Heap.
"""
if new_val > node.val:
raise ValueError("New value is greater than current value")
node.val = new_val
parent = node.parent
if parent and node.val < parent.val:
self._cut(node, parent)
self._cascading_cut(parent)
if node.val < self.min_node.val:
self.min_node = node
def _cut(self, node: FibonacciNode, parent: FibonacciNode) -> None:
"""
Cut a node from its parent in the Fibonacci Heap.
"""
parent.remove_child(node)
self.root_list.append(node)
node.mark = False
def _cascading_cut(self, node: FibonacciNode) -> None:
"""
Perform a cascading cut operation in the Fibonacci Heap.
"""
if parent := node.parent:
if not node.mark:
node.mark = True
else:
self._cut(node, parent)
self._cascading_cut(parent)
def delete_node(self, node: FibonacciNode) -> None:
"""
Delete a specific node from the Fibonacci Heap.
"""
self.decrease_key(node, float("-inf"))
self.delete_min()
def _update_min_node(self) -> None:
"""
Update the minimum node in the Fibonacci Heap.
"""
if not self.root_list:
self.min_node = None
return
min_val = min(node.val for node in self.root_list)
self.min_node = next(node for node in self.root_list if node.val == min_val)
def __str__(self) -> str:
"""
Return a string representation of the Fibonacci Heap.
"""
if self.min_node is None:
return "Fibonacci Heap (Empty)"
return f"Fibonacci Heap (Minimum: {self.min_node.val})"
if __name__ == "__main__":
import doctest
doctest.testmod()