A binomial heap is a forest with at most one heap-ordered tree of each degree. Linking two degree-D trees makes the larger root a child of the smaller root, producing one degree-D-plus-one tree. The forest therefore behaves like a binary count: equal degrees carry into the next degree. This implementation stores roots by degree, merges a singleton or another forest by repeated carries, and extracts the smallest root before returning its children to the forest. It compares priority followed by one process-wide arrival number to preserve stable ties across merged queues. A meld transfers the donor's roots and leaves the donor empty. There is no decrease-key or arbitrary-node deletion API.
Binomial heaps: carry equal-degree trees during merge
Operational case
East and West each hold two incident tasks. Their merge may have two degree-one roots, which must link into one degree-two tree; leaving both roots at degree one breaks the representation invariant. The merged queue returns valve-19 and sensor-61 before pump-47 and grid-83. Emptying the donor is part of the ownership contract, not an optimization detail. A checker can recursively count nodes in each tree and compare its size with two raised to its degree, then verify that every parent priority is no greater than its children.
Working Python program
import itertools
arrival_numbers = itertools.count()
class BinomialNode:
def __init__(self, priority, incident_id):
self.key = (priority, next(arrival_numbers))
self.incident_id = incident_id
self.degree = 0
self.children = []
class BinomialIncidentHeap:
def __init__(self):
self.roots = {}
self.size = 0
@staticmethod
def _link(left, right):
if right.key < left.key:
left, right = right, left
left.children.append(right)
left.degree += 1
return left
def _add_tree(self, tree):
while tree.degree in self.roots:
tree = self._link(tree, self.roots.pop(tree.degree))
self.roots[tree.degree] = tree
def push(self, priority, incident_id):
self._add_tree(BinomialNode(priority, incident_id))
self.size += 1
def meld(self, other):
if other is self:
raise ValueError("cannot meld a heap into itself")
for tree in list(other.roots.values()):
self._add_tree(tree)
self.size += other.size
other.roots.clear()
other.size = 0
def pop(self):
if not self.roots:
raise IndexError("empty priority queue")
degree = min(self.roots, key=lambda level: self.roots[level].key)
removed = self.roots.pop(degree)
for subtree in removed.children:
self._add_tree(subtree)
self.size -= 1
return removed.key[0], removed.incident_id
east = BinomialIncidentHeap()
west = BinomialIncidentHeap()
east.push(47, "pump-47")
east.push(19, "valve-19")
west.push(19, "sensor-61")
west.push(61, "grid-83")
east.meld(west)
print("order=", [east.pop()[1] for _ in range(4)], "donor-size=", west.size, sep="")Output
order=['valve-19', 'sensor-61', 'pump-47', 'grid-83']donor-size=0Time, space, and tradeoff
For N entries, a forest has at most O(log N) roots. Push, meld, and pop perform O(log N) tree links or root scans in the worst-case structural model; finding the minimum here scans the roots rather than storing a separate pointer. Space is O(N) nodes and child references. Python dictionary operations add expected hashing costs, and the stated structural bounds do not promise hard real-time latency. The forest representation pays more pointer overhead than an array heap, but melding it does not require inserting all donor entries one by one. This direct code assumes single-process ownership and does not synchronize concurrent producers.
Common Mistakes
- Do not retain two roots with the same degree after a carry.
- Do not discard the removed root's children during extraction.
- Do not call the donor independently usable after ownership transfer.
- Do not promise constant-time minimum lookup when roots are scanned.
Connected lessons
- Trees and Heaps
- Data Structures
- Pairing heaps: meld roots and pair children on removal
- Binary heaps: select the next priority with a tie rule
- B-trees: split full pages during ordered insertion
- Projects
- Quizzes
Compare its queue operations with Pairing heaps: meld roots and pair children on removal, Radix heaps: queue nondecreasing integer priorities, Double-ended queues: reconcile min and max heaps, then run the priority-queue audit and contract quiz.
Leftist heaps: keep the right spine short for meld adds a distinct structure contract to compare.
Fibonacci heaps: cut on decrease and consolidate on removal adds a related structure with a different operation boundary.
