libcstl
Loading...
Searching...
No Matches
heap.c
Go to the documentation of this file.
1/*!
2 * @file
3 */
4
5#include "cstl/heap.h"
6
7#include <assert.h>
8
9/*!
10 * @private
11 *
12 * given a heap and a numerical id, return the node associated with the id
13 *
14 * Each node in the tree is associated with a numerical identifier with
15 * the root being 0. Each child node is assigned the value of 2 times
16 * its parent's id plus 1 for the left child and plus 2 for the right.
17 *
18 * ex: 0 is the root node. it's children are 1 (left) and 2 (right).
19 * 1's children are 3 (left) and 4 (right).
20 *
21 * ---
22 *
23 * Often, we'll want to find a particular node in the tree by it's id.
24 * Since the tree is a binary tree, the 1's and 0's of the binary
25 * representation of the id can be used to navigate the tree. The issue
26 * is that the bits need to be read from msb to lsb, and it's not obvious
27 * how many bits represent the id.
28 *
29 * To solve this, we add 1 to the id. now the highest set bit, tells us
30 * the number of bits we're dealing with and the remaining bits tell us
31 * to go left (0) or right (1) down the tree to find the particular node.
32 */
33static struct cstl_bintree_node * cstl_heap_find(
34 struct cstl_heap * const h, const unsigned int id)
35{
36 struct cstl_bintree_node * p = h->bt.root;
37
38 const unsigned int loc = id + 1;
39 unsigned int b;
40
41 for (b = (1 << cstl_fls(loc)) >> 1; p != NULL && b != 0; b >>= 1) {
42 if ((loc & b) == 0) {
43 p = p->l;
44 } else {
45 p = p->r;
46 }
47 }
48
49 return p;
50}
51
52/*!
53 * @private
54 *
55 * given a pointer to a node, swap the node with its parent
56 */
57static void cstl_heap_promote_child(struct cstl_heap * const h,
58 struct cstl_bintree_node * const c)
59{
60 struct cstl_bintree_node * const p = c->p;
61 struct cstl_bintree_node * t;
62
63 assert(p != NULL);
64
65 /*
66 * point p's parent to c as one of its children
67 */
68 if (p->p == NULL) {
69 h->bt.root = c;
70 } else if (p->p->l == p) {
71 p->p->l = c;
72 } else {
73 p->p->r = c;
74 }
75
76 /* point c's children to p as their parent */
77 if (c->l != NULL) {
78 c->l->p = p;
79 }
80 if (c->r != NULL) {
81 c->r->p = p;
82 }
83
84 /* point p's children to c as their parent */
85 if (p->r != NULL) {
86 p->r->p = c;
87 }
88 if (p->l != NULL) {
89 p->l->p = c;
90 }
91
92 /*
93 * p's old parent is c's new parent,
94 * and c is p's new parent
95 */
96 c->p = p->p;
97 p->p = c;
98
99 /*
100 * finally, fix the children of each node.
101 * if c was p's left child, then p's new
102 * left child is c's old left child, and
103 * p is c's new left child. the right children
104 * are simply swapped between p and c.
105 *
106 * the case is symmetric/reversed if c
107 * was p's right child
108 */
109 if (p->l == c) {
110 p->l = c->l;
111 c->l = p;
112
113 cstl_swap(&c->r, &p->r, &t, sizeof(t));
114 } else {
115 p->r = c->r;
116 c->r = p;
117
118 cstl_swap(&c->l, &p->l, &t, sizeof(t));
119 }
120}
121
122void cstl_heap_push(struct cstl_heap * const h, void * const p)
123{
124 struct cstl_bintree_node * const n = (void *)((uintptr_t)p + h->bt.off);
125
126 n->l = NULL;
127 n->r = NULL;
128
129 if (h->bt.root == NULL) {
130 n->p = NULL;
131 h->bt.root = n;
132 } else {
133 /*
134 * new nodes are inserted by adding them to the bottom
135 * of the tree and then promoting that node toward the
136 * root until it's in the right spot
137 */
138
139 /* find the parent of the next open spot */
140 n->p = cstl_heap_find(h, (h->bt.size - 1) / 2);
141
142 /*
143 * left children have have odd numbers;
144 * right children have even numbers
145 */
146 if (h->bt.size % 2 == 0) {
147 n->p->r = n;
148 } else {
149 n->p->l = n;
150 }
151
152 /*
153 * while n is greater than its parent,
154 * swap parent and child
155 */
156 while (n->p != NULL
157 && __cstl_bintree_cmp(&h->bt, n, n->p) > 0) {
158 cstl_heap_promote_child(h, n);
159 }
160 }
161
162 h->bt.size++;
163}
164
165const void * cstl_heap_get(const struct cstl_heap * const h)
166{
167 if (h->bt.root != NULL) {
168 return (void *)((uintptr_t)h->bt.root - h->bt.off);
169 }
170
171 return NULL;
172}
173
174void * cstl_heap_pop(struct cstl_heap * const h)
175{
176 void * const res = (void *)cstl_heap_get(h);
177
178 if (res != NULL) {
179 struct cstl_bintree_node * n;
180
181 /*
182 * find the last node in the heap. because it's
183 * at the bottom, it will have no children
184 */
185 n = cstl_heap_find(h, h->bt.size - 1);
186 assert(n->l == NULL && n->r == NULL);
187
188 /*
189 * unlink n from its parent, which reduces
190 * the size of the heap by one
191 */
192 if (n->p == NULL) {
193 h->bt.root = NULL;
194 } else if (n->p->l == n) {
195 n->p->l = NULL;
196 } else {
197 n->p->r = NULL;
198 }
199
200 h->bt.size--;
201
202 if (h->bt.root != NULL) {
203 struct cstl_bintree_node * c;
204
205 /*
206 * if n was not the root node, then
207 * replace the root node with n
208 */
209 *n = *h->bt.root;
210 if (n->l != NULL) {
211 n->l->p = n;
212 }
213 if (n->r != NULL) {
214 n->r->p = n;
215 }
216 h->bt.root = n;
217
218 /*
219 * while either of n's children is greater than n,
220 * swap n with the greater of the two children.
221 */
222 c = NULL;
223 do {
224 if (c != NULL) {
225 cstl_heap_promote_child(h, c);
226 }
227
228 c = n;
229 if (n->l != NULL
230 && __cstl_bintree_cmp(&h->bt, n->l, c) > 0) {
231 c = n->l;
232 }
233 if (n->r != NULL
234 && __cstl_bintree_cmp(&h->bt, n->r, c) > 0) {
235 c = n->r;
236 }
237 } while (n != c);
238 }
239 }
240
241 return res;
242}
243
244#ifdef __cfg_test__
245// GCOV_EXCL_START
246#include <check.h>
247#include <stdlib.h>
248#include <limits.h>
249
250struct integer
251{
252 int v;
253 struct cstl_heap_node hn;
254};
255
256static int cmp_integer(const void * const a, const void * const b,
257 void * const p)
258{
259 (void)p;
260 return ((struct integer *)a)->v - ((struct integer *)b)->v;
261}
262
263static int __cstl_heap_verify(const void * const elem,
264 const cstl_bintree_visit_order_t order,
265 void * const priv)
266{
268 || order == CSTL_BINTREE_VISIT_ORDER_LEAF) {
269 const struct cstl_heap * const h = priv;
270 const struct cstl_heap_node * const hn =
271 &((const struct integer *)elem)->hn;
272
273 if (hn->bn.l != NULL) {
274 ck_assert_int_le(
275 __cstl_bintree_cmp(&h->bt, hn->bn.l, &hn->bn),
276 0);
277 }
278 if (hn->bn.r != NULL) {
279 ck_assert_int_le(
280 __cstl_bintree_cmp(&h->bt, hn->bn.r, &hn->bn),
281 0);
282 }
283 }
284
285 return 0;
286}
287
288static void cstl_heap_verify(const struct cstl_heap * const h)
289{
290 if (h->bt.root != NULL) {
291 size_t min, max;
292
294 __cstl_heap_verify, (void *)h,
296 cstl_bintree_height(&h->bt, &min, &max);
297
298 /*
299 * the tree should always be as compact as
300 * possible. in fact, we could go a step further
301 * to make sure there are no gaps in the leaves
302 */
303
304 ck_assert_uint_le(max - min, 1);
305 ck_assert_uint_le(max, log2(cstl_heap_size(h)) + 1);
306 }
307}
308
309static void __test__cstl_heap_fill(struct cstl_heap * const h, const size_t n)
310{
311 unsigned int i;
312
313 for (i = 0; i < n; i++) {
314 struct integer * const in = malloc(sizeof(*in));
315
316 in->v = rand() % n;
317
318 cstl_heap_push(h, in);
319 ck_assert_uint_eq(i + 1, cstl_heap_size(h));
320 }
321}
322
323static void __test__cstl_heap_drain(struct cstl_heap * const h)
324{
325 size_t sz;
326 int n;
327
328 n = INT_MAX;
329 while ((sz = cstl_heap_size(h)) > 0) {
330 struct integer * in = cstl_heap_pop(h);
331
332 ck_assert_int_le(in->v, n);
333 free(in);
334
335 ck_assert_uint_eq(sz - 1, cstl_heap_size(h));
336
337 cstl_heap_verify(h);
338 }
339
340 ck_assert_ptr_null(h->bt.root);
341 ck_assert_uint_eq(cstl_heap_size(h), 0);
342 ck_assert_ptr_null(cstl_heap_pop(h));
343}
344
345START_TEST(fill)
346{
347 static const size_t n = 100;
348
349 DECLARE_CSTL_HEAP(h, struct integer, hn, cmp_integer, NULL);
350
351 __test__cstl_heap_fill(&h, n);
352 cstl_heap_verify(&h);
353 __test__cstl_heap_drain(&h);
354}
355END_TEST
356
357Suite * heap_suite(void)
358{
359 Suite * const s = suite_create("heap");
360
361 TCase * tc;
362
363 tc = tcase_create("heap");
364 tcase_add_test(tc, fill);
365 suite_add_tcase(s, tc);
366
367 return s;
368}
369
370// GCOV_EXCL_STOP
371#endif
static void cstl_swap(void *const x, void *const y, void *const t, const size_t sz)
Swap values at two memory locations via use of a third.
Definition common.h:137
int cstl_fls(unsigned long)
Find the last (highest order) bit set.
Definition common.c:7
cstl_bintree_visit_order_t
Enumeration indicating the order in which a tree element is being visited during cstl_bintree_foreach...
Definition bintree.h:249
void cstl_bintree_height(const struct cstl_bintree *bt, size_t *min, size_t *max)
Determine the maximum and minimum heights of a tree.
Definition bintree.c:608
int cstl_bintree_foreach(const struct cstl_bintree *bt, cstl_bintree_const_visit_func_t *visit, void *priv, cstl_bintree_foreach_dir_t dir)
Visit each element in a tree, calling a user-defined function for each visit.
Definition bintree.c:475
@ CSTL_BINTREE_VISIT_ORDER_LEAF
The only visit to an element that has no children.
Definition bintree.h:263
@ CSTL_BINTREE_VISIT_ORDER_MID
The second visit to an element, after its first child has/would have been visited.
Definition bintree.h:256
@ CSTL_BINTREE_FOREACH_DIR_FWD
Each element in the tree is visited from left-to-right.
Definition bintree.h:277
void * cstl_heap_pop(struct cstl_heap *const h)
Remove the highest valued element from the heap.
Definition heap.c:174
const void * cstl_heap_get(const struct cstl_heap *const h)
Get a pointer to the object at the top of the heap.
Definition heap.c:165
void cstl_heap_push(struct cstl_heap *const h, void *const p)
Insert a new object into the heap.
Definition heap.c:122
static size_t cstl_heap_size(const struct cstl_heap *const h)
Get the number of objects in the heap.
Definition heap.h:135
#define DECLARE_CSTL_HEAP(NAME, TYPE, MEMB, CMP, PRIV)
(Statically) declare and initialize a heap
Definition heap.h:105
Node to anchor an element within a binary tree.
Definition bintree.h:49
Node to anchor an element within a heap.
Definition heap.h:48
Heap object.
Definition heap.h:62