Fibonacci Heaps
Introduction to AlgorithmsChapter 19: Fibonacci Heaps
A Fibonacci heap is a data structure that supports a set of mergeable heap operations. Several of its operations run in constant amortized time, making it well suited for applications that invoke these operations frequently.
Supported Operations
| Procedure | Binary Heap (worst-case) | Fibonacci Heap (amortized) |
|---|---|---|
| MAKE-HEAP | Θ(1) | Θ(1) |
| INSERT | Θ(lg n) | Θ(1) |
| MINIMUM | Θ(1) | Θ(1) |
| EXTRACT-MIN | Θ(lg n) | O(lg n) |
| UNION | Θ(n) | Θ(1) |
| DECREASE-KEY | Θ(lg n) | Θ(1) |
| DELETE | Θ(lg n) | O(lg n) |
Mergeable Heap Operations
A mergeable heap supports five operations:
- MAKE-HEAP() — creates and returns a new empty heap
- INSERT(H, x) — inserts element x (with key already filled in) into heap H
- MINIMUM(H) — returns a pointer to the element with minimum key
- EXTRACT-MIN(H) — deletes and returns the element with minimum key
- UNION(H1, H2) — creates and returns a new heap containing all elements of H1 and H2, destroying both
Fibonacci heaps additionally support:
- DECREASE-KEY(H, x, k) — assigns element x a new key value k ≤ current key
- DELETE(H, x) — deletes element x from heap H
Practical Considerations
- Fibonacci heaps are especially desirable when EXTRACT-MIN and DELETE operations are rare relative to other operations (e.g., graph algorithms calling DECREASE-KEY once per edge)
- Used in fast algorithms for minimum spanning trees (Ch. 23) and single-source shortest paths (Ch. 24)
- In practice, constant factors and programming complexity make them less desirable than binary/k-ary heaps for most applications
- SEARCH is not efficiently supported — DECREASE-KEY and DELETE require a pointer to the element
Structure of Fibonacci Heaps
A Fibonacci heap is a collection of rooted trees that are min-heap ordered: the key of a node is greater than or equal to the key of its parent.
Node Structure
Each node x contains:
- x.p — pointer to parent
- x.child — pointer to any one of its children
- x.left, x.right — pointers to left and right siblings (children are linked in a circular, doubly linked list called the child list)
- x.degree — number of children in the child list
- x.mark — boolean indicating whether x has lost a child since the last time x was made the child of another node
If node y is an only child, then y.left = y.right = y. Siblings may appear in any order.
Heap Attributes
- H.min — pointer to the root containing the minimum key (the minimum node)
- H.n — total number of nodes in H
- The roots of all trees are linked via left/right pointers into a circular, doubly linked list called the root list
- When H is empty, H.min = NIL
Circular, Doubly Linked Lists
Two key advantages:
- Insert or remove a node from anywhere in O(1) time
- Concatenate (splice) two lists into one in O(1) time
Potential Function
For a Fibonacci heap H:
- t(H) = number of trees in the root list
- m(H) = number of marked nodes
Potential:
Φ(H) = t(H) + 2·m(H)
- Initial potential is 0 (application begins with no heaps)
- Potential is always nonnegative, so total amortized cost upper-bounds total actual cost
Maximum Degree
- D(n) = upper bound on the maximum degree of any node in an n-node Fibonacci heap
- With only mergeable-heap operations: D(n) ≤ ⌊lg n⌋
- With DECREASE-KEY and DELETE supported: D(n) = O(lg n)
Mergeable-Heap Operations
The key design principle: delay work as long as possible. Insertion is O(1) by simply adding to the root list; the deferred consolidation happens during EXTRACT-MIN.
Creating a New Fibonacci Heap
MAKE-FIB-HEAP()
allocate and return H with H.n = 0, H.min = NIL
- t(H) = 0, m(H) = 0, so Φ(H) = 0
- Amortized cost: O(1)
Inserting a Node
FIB-HEAP-INSERT(H, x)
1 x.degree = 0
2 x.p = NIL
3 x.child = NIL
4 x.mark = FALSE
5 if H.min == NIL
6 create a root list for H containing just x
7 H.min = x
8 else insert x into H's root list
9 if x.key < H.min.key
10 H.min = x
11 H.n = H.n + 1
- Initializes node attributes, adds x to the root list, updates H.min if needed
- Potential increase: (t(H)+1 + 2·m(H)) − (t(H) + 2·m(H)) = 1
- Amortized cost: O(1)
Finding the Minimum Node
- Simply return H.min
- No potential change
- Amortized cost: O(1)
Uniting Two Fibonacci Heaps
FIB-HEAP-UNION(H1, H2)
1 H = MAKE-FIB-HEAP()
2 H.min = H1.min
3 concatenate the root list of H2 with the root list of H
4 if (H1.min == NIL) or (H2.min ≠ NIL and H2.min.key < H1.min.key)
5 H.min = H2.min
6 H.n = H1.n + H2.n
7 return H
- Concatenates root lists, determines new minimum, sets H.n
- t(H) = t(H1) + t(H2), m(H) = m(H1) + m(H2), so ΔΦ = 0
- Amortized cost: O(1)
Extracting the Minimum Node
FIB-HEAP-EXTRACT-MIN(H)
1 z = H.min
2 if z ≠ NIL
3 for each child x of z
4 add x to the root list of H
5 x.p = NIL
6 remove z from the root list of H
7 if z == z.right
8 H.min = NIL
9 else H.min = z.right
10 CONSOLIDATE(H)
11 H.n = H.n - 1
12 return z
Steps:
- Make each child of the minimum node a root
- Remove the minimum node from the root list
- If the heap is now empty, set H.min = NIL
- Otherwise, consolidate the root list (link roots of equal degree)
- Decrement H.n and return the removed node
Consolidation
Repeatedly links roots of equal degree until every root has a distinct degree value.
CONSOLIDATE(H)
1 let A[0 .. D(H.n)] be a new array
2 for i = 0 to D(H.n)
3 A[i] = NIL
4 for each node w in the root list of H
5 x = w
6 d = x.degree
7 while A[d] ≠ NIL
8 y = A[d] // another node with same degree as x
9 if x.key > y.key
10 exchange x with y
11 FIB-HEAP-LINK(H, y, x)
12 A[d] = NIL
13 d = d + 1
14 A[d] = x
15 H.min = NIL
16 for i = 0 to D(H.n)
17 if A[i] ≠ NIL
18 if H.min == NIL
19 create a root list for H containing just A[i]
20 H.min = A[i]
21 else insert A[i] into H's root list
22 if A[i].key < H.min.key
23 H.min = A[i]
FIB-HEAP-LINK(H, y, x)
1 remove y from the root list of H
2 make y a child of x, incrementing x.degree
3 y.mark = FALSE
How CONSOLIDATE works:
- Uses auxiliary array A[0 .. D(H.n)] indexed by degree
- Processes each root w: if another root y has the same degree, link the one with the larger key under the smaller, increment degree, repeat
- After processing all roots, rebuild the root list from array A and find the new minimum
Loop invariant for the while loop: at the start of each iteration, d = x.degree.
Amortized Cost of EXTRACT-MIN
- Root list size upon calling CONSOLIDATE: at most D(n) + t(H) − 1
- Total actual work: O(D(n) + t(H))
- Potential before: t(H) + 2·m(H)
- Potential after: at most (D(n) + 1) + 2·m(H)
- Amortized cost:
O(D(n) + t(H)) + (D(n) + 1 + 2·m(H)) − (t(H) + 2·m(H)) = O(D(n)) = O(lg n)
The cost of each link is paid for by the reduction in potential (one fewer root).
Decreasing a Key
FIB-HEAP-DECREASE-KEY
FIB-HEAP-DECREASE-KEY(H, x, k)
1 if k > x.key
2 error "new key is greater than current key"
3 x.key = k
4 y = x.p
5 if y ≠ NIL and x.key < y.key
6 CUT(H, x, y)
7 CASCADING-CUT(H, y)
8 if x.key < H.min.key
9 H.min = x
CUT(H, x, y)
1 remove x from the child list of y, decrementing y.degree
2 add x to the root list of H
3 x.p = NIL
4 x.mark = FALSE
CASCADING-CUT(H, y)
1 z = y.p
2 if z ≠ NIL
3 if y.mark == FALSE
4 y.mark = TRUE
5 else CUT(H, y, z)
6 CASCADING-CUT(H, z)
How It Works
- Assign the new key k to x
- If x is a root or min-heap order is not violated, just update H.min if needed
- Otherwise, cut x from its parent y (make x a root, clear its mark)
- Perform cascading cuts up the tree:
- If parent y is a root → stop
- If y is unmarked → mark it (first child lost) and stop
- If y is marked → cut y from its parent, make y a root, unmark it, recurse on y's parent
Mark Bit Semantics
The mark attribute records history:
- x was a root
- x was linked to (made child of) another node
- One child of x was removed by a cut
When the second child is lost, x is cut from its parent (cascading cut). The mark is cleared whenever a node becomes a root or is linked as a child.
Amortized Cost of DECREASE-KEY
Let c = number of calls to CASCADING-CUT:
- Actual cost: O(c)
- After the operation: t(H) + c trees, at most m(H) − c + 2 marked nodes
- Potential change: at most (t(H) + c + 2(m(H) − c + 2)) − (t(H) + 2·m(H)) = 4 − c
- Amortized cost: O(c) + 4 − c = O(1)
The factor of 2 on marked nodes in the potential function is key: when a marked node is cut, clearing its mark reduces potential by 2 — one unit pays for the cut, the other compensates for the new root.
Deleting a Node
FIB-HEAP-DELETE(H, x)
1 FIB-HEAP-DECREASE-KEY(H, x, -∞)
2 FIB-HEAP-EXTRACT-MIN(H)
- Decrease x's key to −∞, making it the minimum
- Extract the minimum (which is now x)
- Amortized cost: O(1) + O(D(n)) = O(D(n)) = O(lg n)
Bounding the Maximum Degree
The goal: show that D(n) = O(lg n), which validates the amortized bounds for EXTRACT-MIN and DELETE.
Key Idea
For each node x, define size(x) = number of nodes in the subtree rooted at x (including x). We show that size(x) is exponential in x.degree, which bounds the maximum degree logarithmically.
Lemma 19.1
Let x be any node with x.degree = k, and let y₁, y₂, ..., yₖ be the children of x in the order they were linked to x (earliest to latest). Then:
- y₁.degree ≥ 0
- yᵢ.degree ≥ i − 2 for i = 2, 3, ..., k
Proof sketch: When yᵢ was linked to x, x already had children y₁ through yᵢ₋₁, so x.degree ≥ i − 1. Since linking only occurs when degrees match, yᵢ.degree ≥ i − 1 at that time. Since then, yᵢ has lost at most one child (it would have been cut if it lost two), so yᵢ.degree ≥ i − 2.
Fibonacci Numbers Connection
Recall the Fibonacci numbers: F₀ = 0, F₁ = 1, Fₖ = Fₖ₋₁ + Fₖ₋₂ for k ≥ 2.
Lemma 19.2: For all k ≥ 0:
F_{k+2} = 1 + Σᵢ₌₀ᵏ Fᵢ
Lemma 19.3: For all k ≥ 0:
F_{k+2} ≥ φᵏ
where φ = (1 + √5)/2 ≈ 1.61803 is the golden ratio.
Lemma 19.4
Let x be any node in a Fibonacci heap with x.degree = k. Then:
size(x) ≥ F_{k+2} ≥ φᵏ
Proof sketch:
- Let sₖ = minimum possible size of any node of degree k
- s₀ = 1, s₁ = 2
- For the children y₁, ..., yₖ of a degree-k node: size(x) ≥ sₖ ≥ 2 + Σᵢ₌₂ᵏ sᵢ₋₂
- By induction using Lemma 19.1 and Lemma 19.2: sₖ ≥ F_{k+2} ≥ φᵏ
Corollary 19.5
The maximum degree D(n) of any node in an n-node Fibonacci heap is O(lg n).
Proof: From Lemma 19.4, n ≥ size(x) ≥ φᵏ. Taking base-φ logarithms: k ≤ log_φ(n). Since k is an integer, k ≤ ⌊log_φ(n)⌋. Thus D(n) = O(lg n).
This is why the data structure is called a Fibonacci heap — the Fibonacci numbers arise naturally in bounding the maximum degree.
Previous chapter
B TreesNext chapter
van Emde Boas Trees