Skip to content

Commit 9462dc0

Browse files
author
Nikola Yurukov
committed
[Dev] Implement Fibonacci heap
1 parent 377cd6c commit 9462dc0

File tree

1 file changed

+392
-0
lines changed

1 file changed

+392
-0
lines changed

fibheap/fibheap.go

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/*
2+
Licensed under the Apache License, Version 2.0 (the "License");
3+
you may not use this file except in compliance with the License.
4+
You may obtain a copy of the License at
5+
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
8+
Unless required by applicable law or agreed to in writing, software
9+
distributed under the License is distributed on an "AS IS" BASIS,
10+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
See the License for the specific language governing permissions and
12+
limitations under the License.
13+
14+
Special thanks to Keith Schwarz ([email protected]),
15+
whose code and documentation have been used as a reference
16+
for the algorithm implementation.
17+
http://www.keithschwarz.com/interesting/code/?dir=fibonacci-heap
18+
*/
19+
20+
/*
21+
* An implementation of a priority queue backed by a Fibonacci heap,
22+
* as described by Fredman and Tarjan. Fibonacci heaps are interesting
23+
* theoretically because they have asymptotically good runtime guarantees
24+
* for many operations. In particular, insert, peek, and decrease-key all
25+
* run in amortized O(1) time. dequeueMin and delete each run in amortized
26+
* O(lg n) time. This allows algorithms that rely heavily on decrease-key
27+
* to gain significant performance boosts. For example, Dijkstra's algorithm
28+
* for single-source shortest paths can be shown to run in O(m + n lg n) using
29+
* a Fibonacci heap, compared to O(m lg n) using a standard binary or binomial
30+
* heap.
31+
*
32+
* Internally, a Fibonacci heap is represented as a circular, doubly-linked
33+
* list of trees obeying the min-heap property. Each node stores pointers
34+
* to its parent (if any) and some arbitrary child. Additionally, every
35+
* node stores its degree (the number of children it has) and whether it
36+
* is a "marked" node. Finally, each Fibonacci heap stores a pointer to
37+
* the tree with the minimum value.
38+
*
39+
* To insert a node into a Fibonacci heap, a singleton tree is created and
40+
* merged into the rest of the trees. The merge operation works by simply
41+
* splicing together the doubly-linked lists of the two trees, then updating
42+
* the min pointer to be the smaller of the minima of the two heaps. Peeking
43+
* at the smallest element can therefore be accomplished by just looking at
44+
* the min element. All of these operations complete in O(1) time.
45+
*
46+
* The tricky operations are dequeueMin and decreaseKey. dequeueMin works
47+
* by removing the root of the tree containing the smallest element, then
48+
* merging its children with the topmost roots. Then, the roots are scanned
49+
* and merged so that there is only one tree of each degree in the root list.
50+
* This works by maintaining a dynamic array of trees, each initially null,
51+
* pointing to the roots of trees of each dimension. The list is then scanned
52+
* and this array is populated. Whenever a conflict is discovered, the
53+
* appropriate trees are merged together until no more conflicts exist. The
54+
* resulting trees are then put into the root list. A clever analysis using
55+
* the potential method can be used to show that the amortized cost of this
56+
* operation is O(lg n), see "Introduction to Algorithms, Second Edition" by
57+
* Cormen, Rivest, Leiserson, and Stein for more details.
58+
*
59+
* The other hard operation is decreaseKey, which works as follows. First, we
60+
* update the key of the node to be the new value. If this leaves the node
61+
* smaller than its parent, we're done. Otherwise, we cut the node from its
62+
* parent, add it as a root, and then mark its parent. If the parent was
63+
* already marked, we cut that node as well, recursively mark its parent,
64+
* and continue this process. This can be shown to run in O(1) amortized time
65+
* using yet another clever potential function. Finally, given this function,
66+
* we can implement delete by decreasing a key to -\infty, then calling
67+
* dequeueMin to extract it.
68+
*/
69+
70+
package fibheap
71+
72+
import (
73+
"fmt"
74+
"math"
75+
)
76+
77+
/******************************************
78+
************** INTERFACE *****************
79+
******************************************/
80+
81+
// The FloatingFibonacciHeap is an implementation of a fibonacci heap
82+
// with only floating-point priorities and no user data attached.
83+
type FloatingFibonacciHeap interface {
84+
// Adds and element to the heap
85+
Enqueue(priority float64) *Entry
86+
// Returns the minimum element in the heap
87+
Min() (*Entry, error)
88+
// Is the heap empty?
89+
IsEmpty() bool
90+
// The number of elements in the heap
91+
Size() uint
92+
// Removes and returns the minimal element
93+
// in the heap
94+
DequeueMin() (*Entry, error)
95+
// Decreases the key of the given element
96+
// and sets it to the new given priority
97+
// returns the node if succesfully set
98+
DecreaseKey(node *Entry, newPriority float64) (*Entry, error)
99+
// Deletes the given element in the heap
100+
Delete(node *Entry) error
101+
// Will merge two heaps
102+
Merge(otherHeap FloatingFibonacciHeap) (FloatingFibonacciHeap, error)
103+
}
104+
105+
// Entry is the entry type that will be used
106+
// for each node of the Fibonacci heap
107+
type Entry struct {
108+
degree int
109+
marked bool
110+
next, prev, child, parent *Entry
111+
priority float64
112+
}
113+
114+
/******************************************
115+
************** END INTERFACE *************
116+
******************************************/
117+
118+
type fibHeap struct {
119+
min *Entry // The minimal element
120+
size uint // Size of the heap
121+
}
122+
123+
// ****************
124+
// HELPER FUNCTIONS
125+
// ****************
126+
127+
func newEntry(priority float64) *Entry {
128+
result := new(Entry)
129+
result.degree = 0
130+
result.marked = false
131+
result.child = nil
132+
result.parent = nil
133+
result.next = result
134+
result.prev = result
135+
result.priority = priority
136+
return result
137+
}
138+
139+
// ***********
140+
// ACTUAL CODE
141+
// ***********
142+
143+
/*
144+
NewFloatFibHeap creates a new, empty, Fibonacci heap object.
145+
146+
Remember that it's actually *fibHeap and not fibHeap
147+
that fulfills the contract of the interface.
148+
*/
149+
func NewFloatFibHeap() FloatingFibonacciHeap { return &fibHeap{nil, 0} }
150+
151+
func (heap *fibHeap) Enqueue(priority float64) *Entry {
152+
singleton := newEntry(priority)
153+
154+
// Merge singleton list with heap
155+
heap.min = mergeLists(heap.min, singleton)
156+
heap.size++
157+
return singleton
158+
}
159+
160+
func (heap *fibHeap) Min() (*Entry, error) {
161+
if heap.IsEmpty() {
162+
return nil, fmt.Errorf("Trying to get minimum element of empty heap")
163+
}
164+
return heap.min, nil
165+
}
166+
167+
func (heap *fibHeap) IsEmpty() bool {
168+
return heap.size == 0
169+
}
170+
171+
func (heap *fibHeap) Size() uint {
172+
return heap.size
173+
}
174+
175+
func (heap *fibHeap) DequeueMin() (*Entry, error) {
176+
if heap.IsEmpty() {
177+
return nil, fmt.Errorf("Heap is empty")
178+
}
179+
180+
heap.size--
181+
182+
// Copy pointer. Will need it later.
183+
min := heap.min
184+
185+
if min.next == min { // This is the only root node
186+
heap.min = nil
187+
} else { // There are more root nodes
188+
heap.min.prev.next = heap.min.next
189+
heap.min.next.prev = heap.min.prev
190+
heap.min = heap.min.next // Arbitrary element of the root list
191+
}
192+
193+
if min.child != nil {
194+
// Keep track of the first visited node
195+
curr := min.child
196+
for ok := true; ok; ok = (curr != min.child) {
197+
curr.parent = nil
198+
curr = curr.next
199+
}
200+
}
201+
202+
heap.min = mergeLists(heap.min, min.child)
203+
204+
if heap.min == nil {
205+
// If there are no entries left, we're done.
206+
return min, nil
207+
}
208+
209+
treeSlice := make([]*Entry, 0, heap.size)
210+
toVisit := make([]*Entry, 0, heap.size)
211+
212+
for curr := heap.min; len(toVisit) == 0 || toVisit[0] != curr; curr = curr.next {
213+
toVisit = append(toVisit, curr)
214+
}
215+
216+
for _, curr := range toVisit {
217+
for {
218+
for curr.degree >= len(treeSlice) {
219+
treeSlice = append(treeSlice, nil)
220+
}
221+
222+
if treeSlice[curr.degree] == nil {
223+
treeSlice[curr.degree] = curr
224+
break
225+
}
226+
227+
other := treeSlice[curr.degree]
228+
treeSlice[curr.degree] = nil
229+
230+
// Determine which of two trees has the smaller root
231+
var minT, maxT *Entry
232+
if other.priority < curr.priority {
233+
minT = other
234+
maxT = curr
235+
} else {
236+
minT = curr
237+
maxT = other
238+
}
239+
240+
// Break max out of the root list,
241+
// then merge it into min's child list
242+
maxT.next.prev = maxT.prev
243+
maxT.prev.next = maxT.next
244+
245+
// Make it a singleton so that we can merge it
246+
maxT.prev = maxT
247+
maxT.next = maxT
248+
minT.child = mergeLists(minT.child, maxT)
249+
250+
// Reparent max appropriately
251+
maxT.parent = minT
252+
253+
// Clear max's mark, since it can now lose another child
254+
maxT.marked = false
255+
256+
// Increase min's degree. It has another child.
257+
minT.degree++
258+
259+
// Continue merging this tree
260+
curr = minT
261+
}
262+
263+
/* Update the global min based on this node. Note that we compare
264+
* for <= instead of < here. That's because if we just did a
265+
* reparent operation that merged two different trees of equal
266+
* priority, we need to make sure that the min pointer points to
267+
* the root-level one.
268+
*/
269+
if curr.priority <= heap.min.priority {
270+
heap.min = curr
271+
}
272+
}
273+
274+
// All done. Return minimum element and no error
275+
return min, nil
276+
}
277+
278+
func (heap *fibHeap) DecreaseKey(node *Entry, newPriority float64) (*Entry, error) {
279+
280+
if newPriority > node.priority {
281+
return nil, fmt.Errorf("The given new priority is larger than the old")
282+
}
283+
284+
decreaseKeyUnchecked(heap, node, newPriority)
285+
return node, nil
286+
}
287+
288+
func (heap *fibHeap) Delete(node *Entry) error {
289+
290+
decreaseKeyUnchecked(heap, node, -math.MaxFloat64)
291+
heap.DequeueMin()
292+
return nil
293+
}
294+
295+
/*
296+
* Given two Fibonacci heaps, returns a new Fibonacci heap that contains
297+
* all of the elements of the two heaps. Each of the input heaps is
298+
* destructively modified by having all its elements removed. You can
299+
* continue to use those heaps, but be aware that they will be empty
300+
* after this call completes.
301+
*/
302+
func (heap *fibHeap) Merge(other FloatingFibonacciHeap) (FloatingFibonacciHeap, error) {
303+
304+
otherHeap, ok := other.(*fibHeap)
305+
if !ok {
306+
// throw an error
307+
return nil, fmt.Errorf("The passed object is of type %T, not of internal type *fibHeap. Please provide your own implementation of merge", other)
308+
}
309+
310+
resultSize := heap.size + otherHeap.size
311+
312+
resultMin := mergeLists(heap.min, otherHeap.min)
313+
314+
heap.min = nil
315+
otherHeap.min = nil
316+
heap.size = 0
317+
otherHeap.size = 0
318+
319+
return &fibHeap{resultMin, resultSize}, nil
320+
}
321+
322+
func mergeLists(one, two *Entry) *Entry {
323+
if one == nil && two == nil {
324+
return nil
325+
} else if one != nil && two == nil {
326+
return one
327+
} else if one == nil && two != nil {
328+
return two
329+
}
330+
// Both trees non-null; actually do the merge.
331+
oneNext := one.next
332+
one.next = two.next
333+
one.next.prev = one
334+
two.next = oneNext
335+
two.next.prev = two
336+
337+
if one.priority < two.priority {
338+
return one
339+
}
340+
return two
341+
342+
}
343+
344+
func decreaseKeyUnchecked(heap *fibHeap, node *Entry, priority float64) {
345+
node.priority = priority
346+
347+
if node.parent != nil && node.priority <= node.parent.priority {
348+
cutNode(heap, node)
349+
}
350+
351+
if node.priority <= heap.min.priority {
352+
heap.min = node
353+
}
354+
}
355+
356+
func cutNode(heap *fibHeap, node *Entry) {
357+
node.marked = false
358+
359+
if node.parent == nil {
360+
return
361+
}
362+
363+
// Rewire siblings if it has any
364+
if node.next != node {
365+
node.next.prev = node.prev
366+
node.prev.next = node.next
367+
}
368+
369+
// Rewrite pointer if this is the representative child node
370+
if node.parent.child == node {
371+
if node.next != node {
372+
node.parent.child = node.next
373+
} else {
374+
node.parent.child = nil
375+
}
376+
}
377+
378+
node.parent.degree--
379+
380+
node.prev = node
381+
node.next = node
382+
heap.min = mergeLists(heap.min, node)
383+
384+
// cut parent recursively if marked
385+
if node.parent.marked {
386+
cutNode(heap, node.parent)
387+
} else {
388+
node.parent.marked = true
389+
}
390+
391+
node.parent = nil
392+
}

0 commit comments

Comments
 (0)