1 /*-------------------------------------------------------------------------
4 * A simple binary heap implementation
6 * Portions Copyright (c) 2012-2024, PostgreSQL Global Development Group
9 * src/common/binaryheap.c
11 *-------------------------------------------------------------------------
15 #include "postgres_fe.h"
23 #include "common/logging.h"
25 #include "common/hashfn.h"
26 #include "lib/binaryheap.h"
29 * Define parameters for hash table code generation. The interface is *also*
30 * declared in binaryheap.h (to generate the types, which are externally
33 #define SH_PREFIX bh_nodeidx
34 #define SH_ELEMENT_TYPE bh_nodeidx_entry
35 #define SH_KEY_TYPE bh_node_type
37 #define SH_HASH_KEY(tb, key) \
38 hash_bytes((const unsigned char *) &key, sizeof(bh_node_type))
39 #define SH_EQUAL(tb, a, b) (memcmp(&a, &b, sizeof(bh_node_type)) == 0)
40 #define SH_SCOPE extern
42 #define SH_RAW_ALLOCATOR pg_malloc0
45 #define SH_GET_HASH(tb, a) a->hash
47 #include "lib/simplehash.h"
49 static void sift_down(binaryheap
*heap
, int node_off
);
50 static void sift_up(binaryheap
*heap
, int node_off
);
55 * Returns a pointer to a newly-allocated heap with the given initial number
56 * of nodes, and with the heap property defined by the given comparator
57 * function, which will be invoked with the additional argument specified by
60 * If 'indexed' is true, we create a hash table to track each node's
61 * index in the heap, enabling to perform some operations such as
62 * binaryheap_remove_node_ptr() etc.
65 binaryheap_allocate(int num_nodes
, binaryheap_comparator compare
,
66 bool indexed
, void *arg
)
70 heap
= (binaryheap
*) palloc(sizeof(binaryheap
));
71 heap
->bh_space
= num_nodes
;
72 heap
->bh_compare
= compare
;
76 heap
->bh_has_heap_property
= true;
77 heap
->bh_nodes
= (bh_node_type
*) palloc(sizeof(bh_node_type
) * num_nodes
);
78 heap
->bh_nodeidx
= NULL
;
83 heap
->bh_nodeidx
= bh_nodeidx_create(num_nodes
, NULL
);
85 heap
->bh_nodeidx
= bh_nodeidx_create(CurrentMemoryContext
, num_nodes
,
96 * Resets the heap to an empty state, losing its data content but not the
97 * parameters passed at allocation.
100 binaryheap_reset(binaryheap
*heap
)
103 heap
->bh_has_heap_property
= true;
105 if (binaryheap_indexed(heap
))
106 bh_nodeidx_reset(heap
->bh_nodeidx
);
112 * Releases memory used by the given binaryheap.
115 binaryheap_free(binaryheap
*heap
)
117 if (binaryheap_indexed(heap
))
118 bh_nodeidx_destroy(heap
->bh_nodeidx
);
120 pfree(heap
->bh_nodes
);
125 * These utility functions return the offset of the left child, right
126 * child, and parent of the node at the given index, respectively.
128 * The heap is represented as an array of nodes, with the root node
129 * stored at index 0. The left child of node i is at index 2*i+1, and
130 * the right child at 2*i+2. The parent of node i is at index (i-1)/2.
152 * Double the space allocated for nodes.
155 enlarge_node_array(binaryheap
*heap
)
158 heap
->bh_nodes
= repalloc(heap
->bh_nodes
,
159 sizeof(bh_node_type
) * heap
->bh_space
);
163 * Set the given node at the 'index' and track it if required.
165 * Return true if the node's index is already tracked.
168 set_node(binaryheap
*heap
, bh_node_type node
, int index
)
172 /* Set the node to the nodes array */
173 heap
->bh_nodes
[index
] = node
;
175 if (binaryheap_indexed(heap
))
177 bh_nodeidx_entry
*ent
;
179 /* Keep track of the node index */
180 ent
= bh_nodeidx_insert(heap
->bh_nodeidx
, node
, &found
);
188 * Remove the node's index from the hash table if the heap is indexed.
191 delete_nodeidx(binaryheap
*heap
, bh_node_type node
)
193 if (binaryheap_indexed(heap
))
194 bh_nodeidx_delete(heap
->bh_nodeidx
, node
);
198 * Replace the existing node at 'idx' with the given 'new_node'. Also
199 * update their positions accordingly. Note that we assume the new_node's
200 * position is already tracked if enabled, i.e. the new_node is already
201 * present in the heap.
204 replace_node(binaryheap
*heap
, int index
, bh_node_type new_node
)
206 bool found PG_USED_FOR_ASSERTS_ONLY
;
208 /* Quick return if not necessary to move */
209 if (heap
->bh_nodes
[index
] == new_node
)
212 /* Remove the overwritten node's index */
213 delete_nodeidx(heap
, heap
->bh_nodes
[index
]);
216 * Replace it with the given new node. This node's position must also be
217 * tracked as we assume to replace the node with the existing node.
219 found
= set_node(heap
, new_node
, index
);
220 Assert(!binaryheap_indexed(heap
) || found
);
224 * binaryheap_add_unordered
226 * Adds the given datum to the end of the heap's list of nodes in O(1) without
227 * preserving the heap property. This is a convenience to add elements quickly
228 * to a new heap. To obtain a valid heap, one must call binaryheap_build()
232 binaryheap_add_unordered(binaryheap
*heap
, bh_node_type d
)
234 /* make sure enough space for a new node */
235 if (heap
->bh_size
>= heap
->bh_space
)
236 enlarge_node_array(heap
);
238 heap
->bh_has_heap_property
= false;
239 set_node(heap
, d
, heap
->bh_size
);
246 * Assembles a valid heap in O(n) from the nodes added by
247 * binaryheap_add_unordered(). Not needed otherwise.
250 binaryheap_build(binaryheap
*heap
)
254 for (i
= parent_offset(heap
->bh_size
- 1); i
>= 0; i
--)
256 heap
->bh_has_heap_property
= true;
262 * Adds the given datum to the heap in O(log n) time, while preserving
266 binaryheap_add(binaryheap
*heap
, bh_node_type d
)
268 /* make sure enough space for a new node */
269 if (heap
->bh_size
>= heap
->bh_space
)
270 enlarge_node_array(heap
);
272 set_node(heap
, d
, heap
->bh_size
);
274 sift_up(heap
, heap
->bh_size
- 1);
280 * Returns a pointer to the first (root, topmost) node in the heap
281 * without modifying the heap. The caller must ensure that this
282 * routine is not used on an empty heap. Always O(1).
285 binaryheap_first(binaryheap
*heap
)
287 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
288 return heap
->bh_nodes
[0];
292 * binaryheap_remove_first
294 * Removes the first (root, topmost) node in the heap and returns a
295 * pointer to it after rebalancing the heap. The caller must ensure
296 * that this routine is not used on an empty heap. O(log n) worst
300 binaryheap_remove_first(binaryheap
*heap
)
304 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
306 /* extract the root node, which will be the result */
307 result
= heap
->bh_nodes
[0];
309 /* easy if heap contains one element */
310 if (heap
->bh_size
== 1)
313 delete_nodeidx(heap
, result
);
319 * Remove the last node, placing it in the vacated root entry, and sift
320 * the new root node down to its correct position.
322 replace_node(heap
, 0, heap
->bh_nodes
[--heap
->bh_size
]);
329 * binaryheap_remove_node
331 * Removes the nth (zero based) node from the heap. The caller must ensure
332 * that there are at least (n + 1) nodes in the heap. O(log n) worst case.
335 binaryheap_remove_node(binaryheap
*heap
, int n
)
339 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
340 Assert(n
>= 0 && n
< heap
->bh_size
);
342 /* compare last node to the one that is being removed */
343 cmp
= heap
->bh_compare(heap
->bh_nodes
[--heap
->bh_size
],
347 /* remove the last node, placing it in the vacated entry */
348 replace_node(heap
, n
, heap
->bh_nodes
[heap
->bh_size
]);
350 /* sift as needed to preserve the heap property */
358 * binaryheap_remove_node_ptr
360 * Similar to binaryheap_remove_node() but removes the given node. The caller
361 * must ensure that the given node is in the heap. O(log n) worst case.
363 * This function can be used only if the heap is indexed.
366 binaryheap_remove_node_ptr(binaryheap
*heap
, bh_node_type d
)
368 bh_nodeidx_entry
*ent
;
370 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
371 Assert(binaryheap_indexed(heap
));
373 ent
= bh_nodeidx_lookup(heap
->bh_nodeidx
, d
);
376 binaryheap_remove_node(heap
, ent
->index
);
380 * Workhorse for binaryheap_update_up and binaryheap_update_down.
383 resift_node(binaryheap
*heap
, bh_node_type node
, bool sift_dir_up
)
385 bh_nodeidx_entry
*ent
;
387 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
388 Assert(binaryheap_indexed(heap
));
390 ent
= bh_nodeidx_lookup(heap
->bh_nodeidx
, node
);
392 Assert(ent
->index
>= 0 && ent
->index
< heap
->bh_size
);
395 sift_up(heap
, ent
->index
);
397 sift_down(heap
, ent
->index
);
401 * binaryheap_update_up
403 * Sift the given node up after the node's key is updated. The caller must
404 * ensure that the given node is in the heap. O(log n) worst case.
406 * This function can be used only if the heap is indexed.
409 binaryheap_update_up(binaryheap
*heap
, bh_node_type d
)
411 resift_node(heap
, d
, true);
415 * binaryheap_update_down
417 * Sift the given node down after the node's key is updated. The caller must
418 * ensure that the given node is in the heap. O(log n) worst case.
420 * This function can be used only if the heap is indexed.
423 binaryheap_update_down(binaryheap
*heap
, bh_node_type d
)
425 resift_node(heap
, d
, false);
429 * binaryheap_replace_first
431 * Replace the topmost element of a non-empty heap, preserving the heap
432 * property. O(1) in the best case, or O(log n) if it must fall back to
433 * sifting the new node down.
436 binaryheap_replace_first(binaryheap
*heap
, bh_node_type d
)
438 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
440 replace_node(heap
, 0, d
);
442 if (heap
->bh_size
> 1)
447 * Sift a node up to the highest position it can hold according to the
451 sift_up(binaryheap
*heap
, int node_off
)
453 bh_node_type node_val
= heap
->bh_nodes
[node_off
];
456 * Within the loop, the node_off'th array entry is a "hole" that
457 * notionally holds node_val, but we don't actually store node_val there
458 * till the end, saving some unnecessary data copying steps.
460 while (node_off
!= 0)
464 bh_node_type parent_val
;
467 * If this node is smaller than its parent, the heap condition is
468 * satisfied, and we're done.
470 parent_off
= parent_offset(node_off
);
471 parent_val
= heap
->bh_nodes
[parent_off
];
472 cmp
= heap
->bh_compare(node_val
,
479 * Otherwise, swap the parent value with the hole, and go on to check
480 * the node's new parent.
482 set_node(heap
, parent_val
, node_off
);
483 node_off
= parent_off
;
485 /* Re-fill the hole */
486 set_node(heap
, node_val
, node_off
);
490 * Sift a node down from its current position to satisfy the heap
494 sift_down(binaryheap
*heap
, int node_off
)
496 bh_node_type node_val
= heap
->bh_nodes
[node_off
];
499 * Within the loop, the node_off'th array entry is a "hole" that
500 * notionally holds node_val, but we don't actually store node_val there
501 * till the end, saving some unnecessary data copying steps.
505 int left_off
= left_offset(node_off
);
506 int right_off
= right_offset(node_off
);
509 /* Is the left child larger than the parent? */
510 if (left_off
< heap
->bh_size
&&
511 heap
->bh_compare(node_val
,
512 heap
->bh_nodes
[left_off
],
516 /* Is the right child larger than the parent? */
517 if (right_off
< heap
->bh_size
&&
518 heap
->bh_compare(node_val
,
519 heap
->bh_nodes
[right_off
],
522 /* swap with the larger child */
524 heap
->bh_compare(heap
->bh_nodes
[left_off
],
525 heap
->bh_nodes
[right_off
],
527 swap_off
= right_off
;
531 * If we didn't find anything to swap, the heap condition is
532 * satisfied, and we're done.
538 * Otherwise, swap the hole with the child that violates the heap
539 * property; then go on to check its children.
541 set_node(heap
, heap
->bh_nodes
[swap_off
], node_off
);
544 /* Re-fill the hole */
545 set_node(heap
, node_val
, node_off
);