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 "lib/binaryheap.h"
27 static void sift_down(binaryheap
*heap
, int node_off
);
28 static void sift_up(binaryheap
*heap
, int node_off
);
33 * Returns a pointer to a newly-allocated heap that has the capacity to
34 * store the given number of nodes, with the heap property defined by
35 * the given comparator function, which will be invoked with the additional
36 * argument specified by 'arg'.
39 binaryheap_allocate(int capacity
, binaryheap_comparator compare
, void *arg
)
44 sz
= offsetof(binaryheap
, bh_nodes
) + sizeof(bh_node_type
) * capacity
;
45 heap
= (binaryheap
*) palloc(sz
);
46 heap
->bh_space
= capacity
;
47 heap
->bh_compare
= compare
;
51 heap
->bh_has_heap_property
= true;
59 * Resets the heap to an empty state, losing its data content but not the
60 * parameters passed at allocation.
63 binaryheap_reset(binaryheap
*heap
)
66 heap
->bh_has_heap_property
= true;
72 * Releases memory used by the given binaryheap.
75 binaryheap_free(binaryheap
*heap
)
81 * These utility functions return the offset of the left child, right
82 * child, and parent of the node at the given index, respectively.
84 * The heap is represented as an array of nodes, with the root node
85 * stored at index 0. The left child of node i is at index 2*i+1, and
86 * the right child at 2*i+2. The parent of node i is at index (i-1)/2.
108 * binaryheap_add_unordered
110 * Adds the given datum to the end of the heap's list of nodes in O(1) without
111 * preserving the heap property. This is a convenience to add elements quickly
112 * to a new heap. To obtain a valid heap, one must call binaryheap_build()
116 binaryheap_add_unordered(binaryheap
*heap
, bh_node_type d
)
118 if (heap
->bh_size
>= heap
->bh_space
)
121 pg_fatal("out of binary heap slots");
123 elog(ERROR
, "out of binary heap slots");
126 heap
->bh_has_heap_property
= false;
127 heap
->bh_nodes
[heap
->bh_size
] = d
;
134 * Assembles a valid heap in O(n) from the nodes added by
135 * binaryheap_add_unordered(). Not needed otherwise.
138 binaryheap_build(binaryheap
*heap
)
142 for (i
= parent_offset(heap
->bh_size
- 1); i
>= 0; i
--)
144 heap
->bh_has_heap_property
= true;
150 * Adds the given datum to the heap in O(log n) time, while preserving
154 binaryheap_add(binaryheap
*heap
, bh_node_type d
)
156 if (heap
->bh_size
>= heap
->bh_space
)
159 pg_fatal("out of binary heap slots");
161 elog(ERROR
, "out of binary heap slots");
164 heap
->bh_nodes
[heap
->bh_size
] = d
;
166 sift_up(heap
, heap
->bh_size
- 1);
172 * Returns a pointer to the first (root, topmost) node in the heap
173 * without modifying the heap. The caller must ensure that this
174 * routine is not used on an empty heap. Always O(1).
177 binaryheap_first(binaryheap
*heap
)
179 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
180 return heap
->bh_nodes
[0];
184 * binaryheap_remove_first
186 * Removes the first (root, topmost) node in the heap and returns a
187 * pointer to it after rebalancing the heap. The caller must ensure
188 * that this routine is not used on an empty heap. O(log n) worst
192 binaryheap_remove_first(binaryheap
*heap
)
196 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
198 /* extract the root node, which will be the result */
199 result
= heap
->bh_nodes
[0];
201 /* easy if heap contains one element */
202 if (heap
->bh_size
== 1)
209 * Remove the last node, placing it in the vacated root entry, and sift
210 * the new root node down to its correct position.
212 heap
->bh_nodes
[0] = heap
->bh_nodes
[--heap
->bh_size
];
219 * binaryheap_remove_node
221 * Removes the nth (zero based) node from the heap. The caller must ensure
222 * that there are at least (n + 1) nodes in the heap. O(log n) worst case.
225 binaryheap_remove_node(binaryheap
*heap
, int n
)
229 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
230 Assert(n
>= 0 && n
< heap
->bh_size
);
232 /* compare last node to the one that is being removed */
233 cmp
= heap
->bh_compare(heap
->bh_nodes
[--heap
->bh_size
],
237 /* remove the last node, placing it in the vacated entry */
238 heap
->bh_nodes
[n
] = heap
->bh_nodes
[heap
->bh_size
];
240 /* sift as needed to preserve the heap property */
248 * binaryheap_replace_first
250 * Replace the topmost element of a non-empty heap, preserving the heap
251 * property. O(1) in the best case, or O(log n) if it must fall back to
252 * sifting the new node down.
255 binaryheap_replace_first(binaryheap
*heap
, bh_node_type d
)
257 Assert(!binaryheap_empty(heap
) && heap
->bh_has_heap_property
);
259 heap
->bh_nodes
[0] = d
;
261 if (heap
->bh_size
> 1)
266 * Sift a node up to the highest position it can hold according to the
270 sift_up(binaryheap
*heap
, int node_off
)
272 bh_node_type node_val
= heap
->bh_nodes
[node_off
];
275 * Within the loop, the node_off'th array entry is a "hole" that
276 * notionally holds node_val, but we don't actually store node_val there
277 * till the end, saving some unnecessary data copying steps.
279 while (node_off
!= 0)
283 bh_node_type parent_val
;
286 * If this node is smaller than its parent, the heap condition is
287 * satisfied, and we're done.
289 parent_off
= parent_offset(node_off
);
290 parent_val
= heap
->bh_nodes
[parent_off
];
291 cmp
= heap
->bh_compare(node_val
,
298 * Otherwise, swap the parent value with the hole, and go on to check
299 * the node's new parent.
301 heap
->bh_nodes
[node_off
] = parent_val
;
302 node_off
= parent_off
;
304 /* Re-fill the hole */
305 heap
->bh_nodes
[node_off
] = node_val
;
309 * Sift a node down from its current position to satisfy the heap
313 sift_down(binaryheap
*heap
, int node_off
)
315 bh_node_type node_val
= heap
->bh_nodes
[node_off
];
318 * Within the loop, the node_off'th array entry is a "hole" that
319 * notionally holds node_val, but we don't actually store node_val there
320 * till the end, saving some unnecessary data copying steps.
324 int left_off
= left_offset(node_off
);
325 int right_off
= right_offset(node_off
);
326 int swap_off
= left_off
;
328 /* Is the right child larger than the left child? */
329 if (right_off
< heap
->bh_size
&&
330 heap
->bh_compare(heap
->bh_nodes
[left_off
],
331 heap
->bh_nodes
[right_off
],
333 swap_off
= right_off
;
336 * If no children or parent is >= the larger child, heap condition is
337 * satisfied, and we're done.
339 if (left_off
>= heap
->bh_size
||
340 heap
->bh_compare(node_val
,
341 heap
->bh_nodes
[swap_off
],
346 * Otherwise, swap the hole with the child that violates the heap
347 * property; then go on to check its children.
349 heap
->bh_nodes
[node_off
] = heap
->bh_nodes
[swap_off
];
352 /* Re-fill the hole */
353 heap
->bh_nodes
[node_off
] = node_val
;