mirror of https://github.com/postgres/postgres
This performs slightly better, uses less memory, and needs slightly less code in GiST, than the Red-Black tree previously used. Reviewed by Peter Geogheganpull/14/head
parent
699300a146
commit
e7032610f7
@ -0,0 +1,24 @@ |
||||
This directory contains a general purpose data structures, for use anywhere |
||||
in the backend: |
||||
|
||||
binaryheap.c - a binary heap |
||||
|
||||
pairingheap.c - a pairing heap |
||||
|
||||
ilist.c - single and double-linked lists. |
||||
|
||||
stringinfo.c - an extensible string type |
||||
|
||||
|
||||
Aside from the inherent characteristics of the data structures, there are a |
||||
few practical differences between the binary heap and the pairing heap. The |
||||
binary heap is fully allocated at creation, and cannot be expanded beyond the |
||||
allocated size. The pairing heap on the other hand has no inherent maximum |
||||
size, but the caller needs to allocate each element being stored in the heap, |
||||
while the binary heap works with plain Datums or pointers. |
||||
|
||||
The linked-lists in ilist.c can be embedded directly into other structs, as |
||||
opposed to the List interface in nodes/pg_list.h. |
||||
|
||||
In addition to these, there is an implementation of a Red-Black tree in |
||||
src/backend/utils/adt/rbtree.c. |
||||
@ -0,0 +1,274 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* 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-2014, 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). |
||||
*/ |
||||
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); |
||||
} |
||||
|
||||
/*
|
||||
* 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); |
||||
|
||||
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; |
||||
} |
||||
@ -0,0 +1,72 @@ |
||||
/*
|
||||
* pairingheap.h |
||||
* |
||||
* A Pairing Heap implementation |
||||
* |
||||
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group |
||||
* |
||||
* src/include/lib/pairingheap.h |
||||
*/ |
||||
|
||||
#ifndef PAIRINGHEAP_H |
||||
#define PAIRINGHEAP_H |
||||
|
||||
/*
|
||||
* This represents an element stored in the heap. Embed this in a larger |
||||
* struct containing the actual data you're storing. |
||||
* |
||||
* A node can have multiple children, which form a double-linked list. |
||||
* first_child points to the node's first child, and the subsequent children |
||||
* can be found by following the next_sibling pointers. The last child has |
||||
* next_sibling == NULL. The prev_or_parent pointer points to the node's |
||||
* previous sibling, or if the node is its parent's first child, to the |
||||
* parent. |
||||
*/ |
||||
typedef struct pairingheap_node |
||||
{ |
||||
struct pairingheap_node *first_child; |
||||
struct pairingheap_node *next_sibling; |
||||
struct pairingheap_node *prev_or_parent; |
||||
} pairingheap_node; |
||||
|
||||
/*
|
||||
* For a max-heap, the comparator must return <0 iff a < b, 0 iff a == b, |
||||
* and >0 iff a > b. For a min-heap, the conditions are reversed. |
||||
*/ |
||||
typedef int (*pairingheap_comparator) (const pairingheap_node *a, |
||||
const pairingheap_node *b, |
||||
void *arg); |
||||
|
||||
/*
|
||||
* A pairing heap. |
||||
* |
||||
* You can use pairingheap_allocate() to create a new palloc'd heap, or embed |
||||
* this in a larger struct, set ph_compare and ph_arg directly and initialize |
||||
* ph_root to NULL. |
||||
*/ |
||||
typedef struct pairingheap |
||||
{ |
||||
pairingheap_comparator ph_compare; /* comparison function */ |
||||
void *ph_arg; /* opaque argument to ph_compare */ |
||||
pairingheap_node *ph_root; /* current root of the heap */ |
||||
} pairingheap; |
||||
|
||||
extern pairingheap *pairingheap_allocate(pairingheap_comparator compare, |
||||
void *arg); |
||||
extern void pairingheap_free(pairingheap *heap); |
||||
extern void pairingheap_add(pairingheap *heap, pairingheap_node *node); |
||||
extern pairingheap_node *pairingheap_first(pairingheap *heap); |
||||
extern pairingheap_node *pairingheap_remove_first(pairingheap *heap); |
||||
extern void pairingheap_remove(pairingheap *heap, pairingheap_node *node); |
||||
|
||||
/* Resets the heap to be empty. */ |
||||
#define pairingheap_reset(h) ((h)->ph_root = NULL) |
||||
|
||||
/* Is the heap empty? */ |
||||
#define pairingheap_is_empty(h) ((h)->ph_root == NULL) |
||||
|
||||
/* Is there exactly one node in the heap? */ |
||||
#define pairingheap_is_singular(h) \ |
||||
((h)->ph_root && (h)->ph_root->first_child == NULL) |
||||
|
||||
#endif /* PAIRINGHEAP_H */ |
||||
Loading…
Reference in new issue