/*------------------------------------------------------------------------- * * pairingheap.c * A Pairing Heap implementation * * A pairing heap is a data structure that's useful for implementing * priority queues. It is simple to implement, and provides amortized O(1) * insert and find-min operations, and amortized O(log n) delete-min. * * The pairing heap was first described in this paper: * * Michael L. Fredman, Robert Sedgewick, Daniel D. Sleator, and Robert E. * Tarjan. 1986. * The pairing heap: a new form of self-adjusting heap. * Algorithmica 1, 1 (January 1986), pages 111-129. DOI: 10.1007/BF01840439 * * Portions Copyright (c) 2012-2022, PostgreSQL Global Development Group * * IDENTIFICATION * src/backend/lib/pairingheap.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "lib/pairingheap.h" static pairingheap_node *merge(pairingheap *heap, pairingheap_node *a, pairingheap_node *b); static pairingheap_node *merge_children(pairingheap *heap, pairingheap_node *children); /* * pairingheap_allocate * * Returns a pointer to a newly-allocated heap, with the heap property defined * by the given comparator function, which will be invoked with the additional * argument specified by 'arg'. */ pairingheap * pairingheap_allocate(pairingheap_comparator compare, void *arg) { pairingheap *heap; heap = (pairingheap *) palloc(sizeof(pairingheap)); heap->ph_compare = compare; heap->ph_arg = arg; heap->ph_root = NULL; return heap; } /* * pairingheap_free * * Releases memory used by the given pairingheap. * * Note: The nodes in the heap are not freed! */ void pairingheap_free(pairingheap *heap) { pfree(heap); } /* * A helper function to merge two subheaps into one. * * The subheap with smaller value is put as a child of the other one (assuming * a max-heap). * * The next_sibling and prev_or_parent pointers of the input nodes are * ignored. On return, the returned node's next_sibling and prev_or_parent * pointers are garbage. */ static pairingheap_node * merge(pairingheap *heap, pairingheap_node *a, pairingheap_node *b) { if (a == NULL) return b; if (b == NULL) return a; /* swap 'a' and 'b' so that 'a' is the one with larger value */ if (heap->ph_compare(a, b, heap->ph_arg) < 0) { pairingheap_node *tmp; tmp = a; a = b; b = tmp; } /* and put 'b' as a child of 'a' */ if (a->first_child) a->first_child->prev_or_parent = b; b->prev_or_parent = a; b->next_sibling = a->first_child; a->first_child = b; return a; } /* * pairingheap_add * * Adds the given node to the heap in O(1) time. */ void pairingheap_add(pairingheap *heap, pairingheap_node *node) { node->first_child = NULL; /* Link the new node as a new tree */ heap->ph_root = merge(heap, heap->ph_root, node); heap->ph_root->prev_or_parent = NULL; heap->ph_root->next_sibling = NULL; } /* * pairingheap_first * * Returns a pointer to the first (root, topmost) node in the heap without * modifying the heap. The caller must ensure that this routine is not used on * an empty heap. Always O(1). */ pairingheap_node * pairingheap_first(pairingheap *heap) { Assert(!pairingheap_is_empty(heap)); return heap->ph_root; } /* * pairingheap_remove_first * * Removes the first (root, topmost) node in the heap and returns a pointer to * it after rebalancing the heap. The caller must ensure that this routine is * not used on an empty heap. O(log n) amortized. */ pairingheap_node * pairingheap_remove_first(pairingheap *heap) { pairingheap_node *result; pairingheap_node *children; Assert(!pairingheap_is_empty(heap)); /* Remove the root, and form a new heap of its children. */ result = heap->ph_root; children = result->first_child; heap->ph_root = merge_children(heap, children); if (heap->ph_root) { heap->ph_root->prev_or_parent = NULL; heap->ph_root->next_sibling = NULL; } return result; } /* * Remove 'node' from the heap. O(log n) amortized. */ void pairingheap_remove(pairingheap *heap, pairingheap_node *node) { pairingheap_node *children; pairingheap_node *replacement; pairingheap_node *next_sibling; pairingheap_node **prev_ptr; /* * If the removed node happens to be the root node, do it with * pairingheap_remove_first(). */ if (node == heap->ph_root) { (void) pairingheap_remove_first(heap); return; } /* * Before we modify anything, remember the removed node's first_child and * next_sibling pointers. */ children = node->first_child; next_sibling = node->next_sibling; /* * Also find the pointer to the removed node in its previous sibling, or * if this is the first child of its parent, in its parent. */ if (node->prev_or_parent->first_child == node) prev_ptr = &node->prev_or_parent->first_child; else prev_ptr = &node->prev_or_parent->next_sibling; Assert(*prev_ptr == node); /* * If this node has children, make a new subheap of the children and link * the subheap in place of the removed node. Otherwise just unlink this * node. */ if (children) { replacement = merge_children(heap, children); replacement->prev_or_parent = node->prev_or_parent; replacement->next_sibling = node->next_sibling; *prev_ptr = replacement; if (next_sibling) next_sibling->prev_or_parent = replacement; } else { *prev_ptr = next_sibling; if (next_sibling) next_sibling->prev_or_parent = node->prev_or_parent; } } /* * Merge a list of subheaps into a single heap. * * This implements the basic two-pass merging strategy, first forming pairs * from left to right, and then merging the pairs. */ static pairingheap_node * merge_children(pairingheap *heap, pairingheap_node *children) { pairingheap_node *curr, *next; pairingheap_node *pairs; pairingheap_node *newroot; if (children == NULL || children->next_sibling == NULL) return children; /* Walk the subheaps from left to right, merging in pairs */ next = children; pairs = NULL; for (;;) { curr = next; if (curr == NULL) break; if (curr->next_sibling == NULL) { /* last odd node at the end of list */ curr->next_sibling = pairs; pairs = curr; break; } next = curr->next_sibling->next_sibling; /* merge this and the next subheap, and add to 'pairs' list. */ curr = merge(heap, curr, curr->next_sibling); curr->next_sibling = pairs; pairs = curr; } /* * Merge all the pairs together to form a single heap. */ newroot = pairs; next = pairs->next_sibling; while (next) { curr = next; next = curr->next_sibling; newroot = merge(heap, newroot, curr); } return newroot; } /* * A debug function to dump the contents of the heap as a string. * * The 'dumpfunc' callback appends a string representation of a single node * to the StringInfo. 'opaque' can be used to pass more information to the * callback. */ #ifdef PAIRINGHEAP_DEBUG static void pairingheap_dump_recurse(StringInfo buf, pairingheap_node *node, void (*dumpfunc) (pairingheap_node *node, StringInfo buf, void *opaque), void *opaque, int depth, pairingheap_node *prev_or_parent) { while (node) { Assert(node->prev_or_parent == prev_or_parent); appendStringInfoSpaces(buf, depth * 4); dumpfunc(node, buf, opaque); appendStringInfoChar(buf, '\n'); if (node->first_child) pairingheap_dump_recurse(buf, node->first_child, dumpfunc, opaque, depth + 1, node); prev_or_parent = node; node = node->next_sibling; } } char * pairingheap_dump(pairingheap *heap, void (*dumpfunc) (pairingheap_node *node, StringInfo buf, void *opaque), void *opaque) { StringInfoData buf; if (!heap->ph_root) return pstrdup("(empty)"); initStringInfo(&buf); pairingheap_dump_recurse(&buf, heap->ph_root, dumpfunc, opaque, 0, NULL); return buf.data; } #endif