Header And Logo

PostgreSQL
| The world's most advanced open source database.

binaryheap.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * binaryheap.c
00004  *    A simple binary heap implementaion
00005  *
00006  * Portions Copyright (c) 2012-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *    src/backend/lib/binaryheap.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 
00014 #include "postgres.h"
00015 
00016 #include <math.h>
00017 
00018 #include "lib/binaryheap.h"
00019 
00020 static void sift_down(binaryheap *heap, int node_off);
00021 static void sift_up(binaryheap *heap, int node_off);
00022 static inline void swap_nodes(binaryheap *heap, int a, int b);
00023 
00024 /*
00025  * binaryheap_allocate
00026  *
00027  * Returns a pointer to a newly-allocated heap that has the capacity to
00028  * store the given number of nodes, with the heap property defined by
00029  * the given comparator function, which will be invoked with the additional
00030  * argument specified by 'arg'.
00031  */
00032 binaryheap *
00033 binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
00034 {
00035     int         sz;
00036     binaryheap *heap;
00037 
00038     sz = offsetof(binaryheap, bh_nodes) + sizeof(Datum) * capacity;
00039     heap = palloc(sz);
00040     heap->bh_size = 0;
00041     heap->bh_space = capacity;
00042     heap->bh_has_heap_property = true;
00043     heap->bh_compare = compare;
00044     heap->bh_arg = arg;
00045 
00046     return heap;
00047 }
00048 
00049 /*
00050  * binaryheap_free
00051  *
00052  * Releases memory used by the given binaryheap.
00053  */
00054 void
00055 binaryheap_free(binaryheap *heap)
00056 {
00057     pfree(heap);
00058 }
00059 
00060 /*
00061  * These utility functions return the offset of the left child, right
00062  * child, and parent of the node at the given index, respectively.
00063  *
00064  * The heap is represented as an array of nodes, with the root node
00065  * stored at index 0. The left child of node i is at index 2*i+1, and
00066  * the right child at 2*i+2. The parent of node i is at index (i-1)/2.
00067  */
00068 
00069 static inline int
00070 left_offset(int i)
00071 {
00072     return 2 * i + 1;
00073 }
00074 
00075 static inline int
00076 right_offset(int i)
00077 {
00078     return 2 * i + 2;
00079 }
00080 
00081 static inline int
00082 parent_offset(int i)
00083 {
00084     return (i - 1) / 2;
00085 }
00086 
00087 /*
00088  * binaryheap_add_unordered
00089  *
00090  * Adds the given datum to the end of the heap's list of nodes in O(1) without
00091  * preserving the heap property. This is a convenience to add elements quickly
00092  * to a new heap. To obtain a valid heap, one must call binaryheap_build()
00093  * afterwards.
00094  */
00095 void
00096 binaryheap_add_unordered(binaryheap *heap, Datum d)
00097 {
00098     if (heap->bh_size >= heap->bh_space)
00099         elog(ERROR, "out of binary heap slots");
00100     heap->bh_has_heap_property = false;
00101     heap->bh_nodes[heap->bh_size] = d;
00102     heap->bh_size++;
00103 }
00104 
00105 /*
00106  * binaryheap_build
00107  *
00108  * Assembles a valid heap in O(n) from the nodes added by
00109  * binaryheap_add_unordered(). Not needed otherwise.
00110  */
00111 void
00112 binaryheap_build(binaryheap *heap)
00113 {
00114     int         i;
00115 
00116     for (i = parent_offset(heap->bh_size - 1); i >= 0; i--)
00117         sift_down(heap, i);
00118     heap->bh_has_heap_property = true;
00119 }
00120 
00121 /*
00122  * binaryheap_add
00123  *
00124  * Adds the given datum to the heap in O(log n) time, while preserving
00125  * the heap property.
00126  */
00127 void
00128 binaryheap_add(binaryheap *heap, Datum d)
00129 {
00130     if (heap->bh_size >= heap->bh_space)
00131         elog(ERROR, "out of binary heap slots");
00132     heap->bh_nodes[heap->bh_size] = d;
00133     heap->bh_size++;
00134     sift_up(heap, heap->bh_size - 1);
00135 }
00136 
00137 /*
00138  * binaryheap_first
00139  *
00140  * Returns a pointer to the first (root, topmost) node in the heap
00141  * without modifying the heap. The caller must ensure that this
00142  * routine is not used on an empty heap. Always O(1).
00143  */
00144 Datum
00145 binaryheap_first(binaryheap *heap)
00146 {
00147     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
00148     return heap->bh_nodes[0];
00149 }
00150 
00151 /*
00152  * binaryheap_remove_first
00153  *
00154  * Removes the first (root, topmost) node in the heap and returns a
00155  * pointer to it after rebalancing the heap. The caller must ensure
00156  * that this routine is not used on an empty heap. O(log n) worst
00157  * case.
00158  */
00159 Datum
00160 binaryheap_remove_first(binaryheap *heap)
00161 {
00162     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
00163 
00164     if (heap->bh_size == 1)
00165     {
00166         heap->bh_size--;
00167         return heap->bh_nodes[0];
00168     }
00169 
00170     /*
00171      * Swap the root and last nodes, decrease the size of the heap (i.e.
00172      * remove the former root node) and sift the new root node down to its
00173      * correct position.
00174      */
00175     swap_nodes(heap, 0, heap->bh_size - 1);
00176     heap->bh_size--;
00177     sift_down(heap, 0);
00178 
00179     return heap->bh_nodes[heap->bh_size];
00180 }
00181 
00182 /*
00183  * binaryheap_replace_first
00184  *
00185  * Replace the topmost element of a non-empty heap, preserving the heap
00186  * property.  O(1) in the best case, or O(log n) if it must fall back to
00187  * sifting the new node down.
00188  */
00189 void
00190 binaryheap_replace_first(binaryheap *heap, Datum d)
00191 {
00192     Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
00193 
00194     heap->bh_nodes[0] = d;
00195 
00196     if (heap->bh_size > 1)
00197         sift_down(heap, 0);
00198 }
00199 
00200 /*
00201  * Swap the contents of two nodes.
00202  */
00203 static inline void
00204 swap_nodes(binaryheap *heap, int a, int b)
00205 {
00206     Datum   swap;
00207 
00208     swap = heap->bh_nodes[a];
00209     heap->bh_nodes[a] = heap->bh_nodes[b];
00210     heap->bh_nodes[b] = swap;
00211 }
00212 
00213 /*
00214  * Sift a node up to the highest position it can hold according to the
00215  * comparator.
00216  */
00217 static void
00218 sift_up(binaryheap *heap, int node_off)
00219 {
00220     while (node_off != 0)
00221     {
00222         int         cmp;
00223         int         parent_off;
00224 
00225         /*
00226          * If this node is smaller than its parent, the heap condition is
00227          * satisfied, and we're done.
00228          */
00229         parent_off = parent_offset(node_off);
00230         cmp = heap->bh_compare(heap->bh_nodes[node_off],
00231                                heap->bh_nodes[parent_off],
00232                                heap->bh_arg);
00233         if (cmp <= 0)
00234             break;
00235 
00236         /*
00237          * Otherwise, swap the node and its parent and go on to check the
00238          * node's new parent.
00239          */
00240         swap_nodes(heap, node_off, parent_off);
00241         node_off = parent_off;
00242     }
00243 }
00244 
00245 /*
00246  * Sift a node down from its current position to satisfy the heap
00247  * property.
00248  */
00249 static void
00250 sift_down(binaryheap *heap, int node_off)
00251 {
00252     while (true)
00253     {
00254         int         left_off = left_offset(node_off);
00255         int         right_off = right_offset(node_off);
00256         int         swap_off = 0;
00257 
00258         /* Is the left child larger than the parent? */
00259         if (left_off < heap->bh_size &&
00260             heap->bh_compare(heap->bh_nodes[node_off],
00261                              heap->bh_nodes[left_off],
00262                              heap->bh_arg) < 0)
00263             swap_off = left_off;
00264 
00265         /* Is the right child larger than the parent? */
00266         if (right_off < heap->bh_size &&
00267             heap->bh_compare(heap->bh_nodes[node_off],
00268                              heap->bh_nodes[right_off],
00269                              heap->bh_arg) < 0)
00270         {
00271             /* swap with the larger child */
00272             if (!swap_off ||
00273                 heap->bh_compare(heap->bh_nodes[left_off],
00274                                  heap->bh_nodes[right_off],
00275                                  heap->bh_arg) < 0)
00276                 swap_off = right_off;
00277         }
00278 
00279         /*
00280          * If we didn't find anything to swap, the heap condition is
00281          * satisfied, and we're done.
00282          */
00283         if (!swap_off)
00284             break;
00285 
00286         /*
00287          * Otherwise, swap the node with the child that violates the heap
00288          * property; then go on to check its children.
00289          */
00290         swap_nodes(heap, swap_off, node_off);
00291         node_off = swap_off;
00292     }
00293 }