postgresql/src/backend/lib/pairingheap.c

334 lines
7.7 KiB
C

/*-------------------------------------------------------------------------
*
* 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-2024, 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