Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * binaryheap.c
4 : * A simple binary heap implementation
5 : *
6 : * Portions Copyright (c) 2012-2024, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/common/binaryheap.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 :
14 : #ifdef FRONTEND
15 : #include "postgres_fe.h"
16 : #else
17 : #include "postgres.h"
18 : #endif
19 :
20 : #include <math.h>
21 :
22 : #ifdef FRONTEND
23 : #include "common/logging.h"
24 : #endif
25 : #include "lib/binaryheap.h"
26 :
27 : static void sift_down(binaryheap *heap, int node_off);
28 : static void sift_up(binaryheap *heap, int node_off);
29 :
30 : /*
31 : * binaryheap_allocate
32 : *
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'.
37 : */
38 : binaryheap *
39 7374 : binaryheap_allocate(int capacity, binaryheap_comparator compare, void *arg)
40 : {
41 : int sz;
42 : binaryheap *heap;
43 :
44 7374 : sz = offsetof(binaryheap, bh_nodes) + sizeof(bh_node_type) * capacity;
45 7374 : heap = (binaryheap *) palloc(sz);
46 7374 : heap->bh_space = capacity;
47 7374 : heap->bh_compare = compare;
48 7374 : heap->bh_arg = arg;
49 :
50 7374 : heap->bh_size = 0;
51 7374 : heap->bh_has_heap_property = true;
52 :
53 7374 : return heap;
54 : }
55 :
56 : /*
57 : * binaryheap_reset
58 : *
59 : * Resets the heap to an empty state, losing its data content but not the
60 : * parameters passed at allocation.
61 : */
62 : void
63 374 : binaryheap_reset(binaryheap *heap)
64 : {
65 374 : heap->bh_size = 0;
66 374 : heap->bh_has_heap_property = true;
67 374 : }
68 :
69 : /*
70 : * binaryheap_free
71 : *
72 : * Releases memory used by the given binaryheap.
73 : */
74 : void
75 6492 : binaryheap_free(binaryheap *heap)
76 : {
77 6492 : pfree(heap);
78 6492 : }
79 :
80 : /*
81 : * These utility functions return the offset of the left child, right
82 : * child, and parent of the node at the given index, respectively.
83 : *
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.
87 : */
88 :
89 : static inline int
90 22091824 : left_offset(int i)
91 : {
92 22091824 : return 2 * i + 1;
93 : }
94 :
95 : static inline int
96 22091824 : right_offset(int i)
97 : {
98 22091824 : return 2 * i + 2;
99 : }
100 :
101 : static inline int
102 5743626 : parent_offset(int i)
103 : {
104 5743626 : return (i - 1) / 2;
105 : }
106 :
107 : /*
108 : * binaryheap_add_unordered
109 : *
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()
113 : * afterwards.
114 : */
115 : void
116 57328 : binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
117 : {
118 57328 : if (heap->bh_size >= heap->bh_space)
119 : {
120 : #ifdef FRONTEND
121 0 : pg_fatal("out of binary heap slots");
122 : #else
123 0 : elog(ERROR, "out of binary heap slots");
124 : #endif
125 : }
126 57328 : heap->bh_has_heap_property = false;
127 57328 : heap->bh_nodes[heap->bh_size] = d;
128 57328 : heap->bh_size++;
129 57328 : }
130 :
131 : /*
132 : * binaryheap_build
133 : *
134 : * Assembles a valid heap in O(n) from the nodes added by
135 : * binaryheap_add_unordered(). Not needed otherwise.
136 : */
137 : void
138 6976 : binaryheap_build(binaryheap *heap)
139 : {
140 : int i;
141 :
142 37872 : for (i = parent_offset(heap->bh_size - 1); i >= 0; i--)
143 30896 : sift_down(heap, i);
144 6976 : heap->bh_has_heap_property = true;
145 6976 : }
146 :
147 : /*
148 : * binaryheap_add
149 : *
150 : * Adds the given datum to the heap in O(log n) time, while preserving
151 : * the heap property.
152 : */
153 : void
154 2129410 : binaryheap_add(binaryheap *heap, bh_node_type d)
155 : {
156 2129410 : if (heap->bh_size >= heap->bh_space)
157 : {
158 : #ifdef FRONTEND
159 0 : pg_fatal("out of binary heap slots");
160 : #else
161 0 : elog(ERROR, "out of binary heap slots");
162 : #endif
163 : }
164 2129410 : heap->bh_nodes[heap->bh_size] = d;
165 2129410 : heap->bh_size++;
166 2129410 : sift_up(heap, heap->bh_size - 1);
167 2129410 : }
168 :
169 : /*
170 : * binaryheap_first
171 : *
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).
175 : */
176 : bh_node_type
177 2050042 : binaryheap_first(binaryheap *heap)
178 : {
179 : Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
180 2050042 : return heap->bh_nodes[0];
181 : }
182 :
183 : /*
184 : * binaryheap_remove_first
185 : *
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
189 : * case.
190 : */
191 : bh_node_type
192 2186424 : binaryheap_remove_first(binaryheap *heap)
193 : {
194 : bh_node_type result;
195 :
196 : Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
197 :
198 : /* extract the root node, which will be the result */
199 2186424 : result = heap->bh_nodes[0];
200 :
201 : /* easy if heap contains one element */
202 2186424 : if (heap->bh_size == 1)
203 : {
204 9120 : heap->bh_size--;
205 9120 : return result;
206 : }
207 :
208 : /*
209 : * Remove the last node, placing it in the vacated root entry, and sift
210 : * the new root node down to its correct position.
211 : */
212 2177304 : heap->bh_nodes[0] = heap->bh_nodes[--heap->bh_size];
213 2177304 : sift_down(heap, 0);
214 :
215 2177304 : return result;
216 : }
217 :
218 : /*
219 : * binaryheap_remove_node
220 : *
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.
223 : */
224 : void
225 92 : binaryheap_remove_node(binaryheap *heap, int n)
226 : {
227 : int cmp;
228 :
229 : Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
230 : Assert(n >= 0 && n < heap->bh_size);
231 :
232 : /* compare last node to the one that is being removed */
233 92 : cmp = heap->bh_compare(heap->bh_nodes[--heap->bh_size],
234 : heap->bh_nodes[n],
235 : heap->bh_arg);
236 :
237 : /* remove the last node, placing it in the vacated entry */
238 92 : heap->bh_nodes[n] = heap->bh_nodes[heap->bh_size];
239 :
240 : /* sift as needed to preserve the heap property */
241 92 : if (cmp > 0)
242 0 : sift_up(heap, n);
243 92 : else if (cmp < 0)
244 64 : sift_down(heap, n);
245 92 : }
246 :
247 : /*
248 : * binaryheap_replace_first
249 : *
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.
253 : */
254 : void
255 1625040 : binaryheap_replace_first(binaryheap *heap, bh_node_type d)
256 : {
257 : Assert(!binaryheap_empty(heap) && heap->bh_has_heap_property);
258 :
259 1625040 : heap->bh_nodes[0] = d;
260 :
261 1625040 : if (heap->bh_size > 1)
262 625734 : sift_down(heap, 0);
263 1625040 : }
264 :
265 : /*
266 : * Sift a node up to the highest position it can hold according to the
267 : * comparator.
268 : */
269 : static void
270 2129410 : sift_up(binaryheap *heap, int node_off)
271 : {
272 2129410 : bh_node_type node_val = heap->bh_nodes[node_off];
273 :
274 : /*
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.
278 : */
279 5948404 : while (node_off != 0)
280 : {
281 : int cmp;
282 : int parent_off;
283 : bh_node_type parent_val;
284 :
285 : /*
286 : * If this node is smaller than its parent, the heap condition is
287 : * satisfied, and we're done.
288 : */
289 5736650 : parent_off = parent_offset(node_off);
290 5736650 : parent_val = heap->bh_nodes[parent_off];
291 5736650 : cmp = heap->bh_compare(node_val,
292 : parent_val,
293 : heap->bh_arg);
294 5736650 : if (cmp <= 0)
295 1917656 : break;
296 :
297 : /*
298 : * Otherwise, swap the parent value with the hole, and go on to check
299 : * the node's new parent.
300 : */
301 3818994 : heap->bh_nodes[node_off] = parent_val;
302 3818994 : node_off = parent_off;
303 : }
304 : /* Re-fill the hole */
305 2129410 : heap->bh_nodes[node_off] = node_val;
306 2129410 : }
307 :
308 : /*
309 : * Sift a node down from its current position to satisfy the heap
310 : * property.
311 : */
312 : static void
313 2833998 : sift_down(binaryheap *heap, int node_off)
314 : {
315 2833998 : bh_node_type node_val = heap->bh_nodes[node_off];
316 :
317 : /*
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.
321 : */
322 : while (true)
323 19257826 : {
324 22091824 : int left_off = left_offset(node_off);
325 22091824 : int right_off = right_offset(node_off);
326 22091824 : int swap_off = left_off;
327 :
328 : /* Is the right child larger than the left child? */
329 41773138 : if (right_off < heap->bh_size &&
330 19681314 : heap->bh_compare(heap->bh_nodes[left_off],
331 : heap->bh_nodes[right_off],
332 : heap->bh_arg) < 0)
333 9584754 : swap_off = right_off;
334 :
335 : /*
336 : * If no children or parent is >= the larger child, heap condition is
337 : * satisfied, and we're done.
338 : */
339 42489548 : if (left_off >= heap->bh_size ||
340 20397724 : heap->bh_compare(node_val,
341 : heap->bh_nodes[swap_off],
342 : heap->bh_arg) >= 0)
343 : break;
344 :
345 : /*
346 : * Otherwise, swap the hole with the child that violates the heap
347 : * property; then go on to check its children.
348 : */
349 19257826 : heap->bh_nodes[node_off] = heap->bh_nodes[swap_off];
350 19257826 : node_off = swap_off;
351 : }
352 : /* Re-fill the hole */
353 2833998 : heap->bh_nodes[node_off] = node_val;
354 2833998 : }
|