1 LIBAVL(3LIB) Interface Libraries LIBAVL(3LIB) 2 3 NAME 4 libavl - generic self-balancing binary search tree library 5 6 SYNOPSIS 7 AVL Tree Library (libavl, -lavl) 8 #include <sys/avl.h> 9 10 DESCRIPTION 11 The libavl library provides a generic implementation of AVL trees, a form 12 of self-balancing binary tree. The interfaces provided allow for an 13 efficient way of implementing an ordered set of data structures and, due 14 to its embeddable nature, allow for a single instance of a data structure 15 to belong to multiple AVL trees. 16 17 Each AVL tree contains entries of a single type of data structure. 18 Rather than allocating memory for pointers for those data structures, the 19 storage for the tree is embedded into the data structures by declaring a 20 member of type avl_node_t. When an AVL tree is created, through the use 21 of avl_create(), it encodes the size of the data structure, the offset of 22 the data structure, and a comparator function which is used to compare 23 two instances of a data structure. A data structure may be a member of 24 multiple AVL trees by creating AVL trees which use different offsets 25 (different members) into the data structure. 26 27 AVL trees support both look up of an arbitrary item and ordered iteration 28 over the contents of the entire tree. In addition, from any node, you can 29 find the previous and next entries in the tree, if they exist. In 30 addition, AVL trees support arbitrary insertion and deletion. 31 32 Performance 33 AVL trees are often used in place of linked lists. Compared to the 34 standard, intrusive, doubly linked list, it has the following performance 35 characteristics: 36 37 Lookup One Node 38 Lookup of a single node in a linked list is O(n), whereas lookup 39 of a single node in an AVL tree is O(log(n)). 40 41 Insert One Node 42 Inserting a single node into a linked list is O(1). Inserting a 43 single node into an AVL tree is O(log(n)). 44 45 Note, insertions into an AVL tree always result in an ordered 46 tree. Insertions into a linked list do not guarantee order. If 47 order is required, then the time to do the insertion into a 48 linked list will depend on the time of the search algorithm being 49 employed to find the place to insert at. 50 51 Delete One Node 52 Deleting a single node from a linked list is O(1), whereas 53 deleting a single node from an AVL tree takes O(log(n)) time. 54 55 Delete All Nodes 56 Deleting all nodes from a linked list is O(n). With an AVL tree, 57 if using the avl_destroy_nodes(3AVL) function then deleting all 58 nodes is O(n). However, if iterating over each entry in the tree 59 and then removing it using a while loop, avl_first(3AVL) and 60 avl_remove(3AVL) then the time to remove all nodes is 61 O(n * log(n)). 62 63 Visit the Next or Previous Node 64 Visiting the next or previous node in a linked list is O(1), 65 whereas going from the next to the previous node in an AVL tree 66 will take between O(1) and O(log(n)). 67 68 In general, AVL trees are a good alternative for linked lists when order 69 or lookup speed is important and a reasonable number of items will be 70 present. 71 72 INTERFACES 73 The shared object libavl.so.1 provides the public interfaces defined 74 below. See Intro(3) for additional information on shared object 75 interfaces. Individual functions are documented in their own manual 76 pages. 77 78 avl_add avl_create 79 avl_destroy avl_destroy_nodes 80 avl_find avl_first 81 avl_insert avl_insert_here 82 avl_is_empty avl_last 83 avl_nearest avl_numnodes 84 avl_remove avl_swap 85 86 In addition, the library defines C pre-processor macros which are defined 87 below and documented in their own manual pages. 88 89 AVL_NEXT AVL_PREV 90 91 TYPES 92 The libavl library defines the following types: 93 94 avl_tree_t 95 96 Type used for the root of the AVL tree. Consumers define one of these for 97 each of the different trees that they want to have. 98 99 avl_node_t 100 101 Type used as the data node for an AVL tree. One of these is embedded in 102 each data structure that is the member of an AVL tree. 103 104 avl_index_t 105 106 Type used to locate a position in a tree. This is used with 107 avl_find(3AVL) and avl_insert(3AVL). 108 109 LOCKING 110 The libavl library provides no locking. Callers that are using the same 111 AVL tree from multiple threads need to provide their own synchronization. 112 If only one thread is ever accessing or modifying the AVL tree, then 113 there are no synchronization concerns. If multiple AVL trees exist, then 114 they may all be used simultaneously; however, they are subject to the 115 same rules around simultaneous access from a single thread. 116 117 All routines are both Fork-safe and Async-Signal-Safe. Callers may call 118 functions in libavl from a signal handler and libavl calls are all safe 119 in face of fork(2); however, if callers have their own locks, then they 120 must make sure that they are accounted for by the use of routines such as 121 pthread_atfork(3C). 122 123 EXAMPLES 124 The following code shows examples of exercising all of the functionality 125 that is present in libavl. It can be compiled by using a C compiler and 126 linking against libavl. For example, given a file named avl.c, with gcc, 127 one would run: 128 129 $ gcc -Wall -o avl avl.c -lavl 130 131 /* 132 * Example of using AVL Trees 133 */ 134 135 #include <sys/avl.h> 136 #include <stddef.h> 137 #include <stdlib.h> 138 #include <stdio.h> 139 140 static avl_tree_t inttree; 141 142 /* 143 * The structure that we're storing in an AVL tree. 144 */ 145 typedef struct intnode { 146 int in_val; 147 avl_node_t in_avl; 148 } intnode_t; 149 150 static int 151 intnode_comparator(const void *l, const void *r) 152 { 153 const intnode_t *li = l; 154 const intnode_t *ri = r; 155 156 if (li->in_val > ri->in_val) 157 return (1); 158 if (li->in_val < ri->in_val) 159 return (-1); 160 return (0); 161 } 162 163 /* 164 * Create an AVL Tree 165 */ 166 static void 167 create_avl(void) 168 { 169 avl_create(&inttree, intnode_comparator, sizeof (intnode_t), 170 offsetof(intnode_t, in_avl)); 171 } 172 173 /* 174 * Add entries to the tree with the avl_add function. 175 */ 176 static void 177 fill_avl(void) 178 { 179 int i; 180 intnode_t *inp; 181 182 for (i = 0; i < 20; i++) { 183 inp = malloc(sizeof (intnode_t)); 184 assert(inp != NULL); 185 inp->in_val = i; 186 avl_add(&inttree, inp); 187 } 188 } 189 190 /* 191 * Find entries in the AVL tree. Note, we create an intnode_t on the 192 * stack that we use to look this up. 193 */ 194 static void 195 find_avl(void) 196 { 197 int i; 198 intnode_t lookup, *inp; 199 200 for (i = 10; i < 30; i++) { 201 lookup.in_val = i; 202 inp = avl_find(&inttree, &lookup, NULL); 203 if (inp == NULL) { 204 printf("Entry %d is not in the tree\n", i); 205 } else { 206 printf("Entry %d is in the tree\n", 207 inp->in_val); 208 } 209 } 210 } 211 212 /* 213 * Walk the tree forwards 214 */ 215 static void 216 walk_forwards(void) 217 { 218 intnode_t *inp; 219 for (inp = avl_first(&inttree); inp != NULL; 220 inp = AVL_NEXT(&inttree, inp)) { 221 printf("Found entry %d\n", inp->in_val); 222 } 223 } 224 225 /* 226 * Walk the tree backwards. 227 */ 228 static void 229 walk_backwards(void) 230 { 231 intnode_t *inp; 232 for (inp = avl_last(&inttree); inp != NULL; 233 inp = AVL_PREV(&inttree, inp)) { 234 printf("Found entry %d\n", inp->in_val); 235 } 236 } 237 238 /* 239 * Determine the number of nodes in the tree and if it is empty or 240 * not. 241 */ 242 static void 243 inttree_inspect(void) 244 { 245 printf("The tree is %s, there are %ld nodes in it\n", 246 avl_is_empty(&inttree) == B_TRUE ? "empty" : "not empty", 247 avl_numnodes(&inttree)); 248 } 249 250 /* 251 * Use avl_remove to remove entries from the tree. 252 */ 253 static void 254 remove_nodes(void) 255 { 256 int i; 257 intnode_t lookup, *inp; 258 259 for (i = 0; i < 20; i+= 4) { 260 lookup.in_val = i; 261 inp = avl_find(&inttree, &lookup, NULL); 262 if (inp != NULL) 263 avl_remove(&inttree, inp); 264 } 265 } 266 267 /* 268 * Find the nearest nodes in the tree. 269 */ 270 static void 271 nearest_nodes(void) 272 { 273 intnode_t lookup, *inp; 274 avl_index_t where; 275 276 lookup.in_val = 12; 277 if (avl_find(&inttree, &lookup, &where) != NULL) 278 abort(); 279 inp = avl_nearest(&inttree, where, AVL_BEFORE); 280 assert(inp != NULL); 281 printf("closest node before 12 is: %d\n", inp->in_val); 282 inp = avl_nearest(&inttree, where, AVL_AFTER); 283 assert(inp != NULL); 284 printf("closest node after 12 is: %d\n", inp->in_val); 285 } 286 287 static void 288 insert_avl(void) 289 { 290 intnode_t lookup, *inp; 291 avl_index_t where; 292 293 lookup.in_val = 12; 294 if (avl_find(&inttree, &lookup, &where) != NULL) 295 abort(); 296 inp = malloc(sizeof (intnode_t)); 297 assert(inp != NULL); 298 avl_insert(&inttree, inp, where); 299 } 300 301 static void 302 swap_avl(void) 303 { 304 avl_tree_t swap; 305 306 avl_create(&swap, intnode_comparator, sizeof (intnode_t), 307 offsetof(intnode_t, in_avl)); 308 avl_swap(&inttree, &swap); 309 inttree_inspect(); 310 avl_swap(&inttree, &swap); 311 inttree_inspect(); 312 } 313 314 /* 315 * Remove all remaining nodes in the tree. We first use 316 * avl_destroy_nodes to empty the tree, then avl_destroy to finish. 317 */ 318 static void 319 cleanup(void) 320 { 321 intnode_t *inp; 322 void *c = NULL; 323 324 while ((inp = avl_destroy_nodes(&inttree, &c)) != NULL) { 325 free(inp); 326 } 327 avl_destroy(&inttree); 328 } 329 330 int 331 main(void) 332 { 333 create_avl(); 334 inttree_inspect(); 335 fill_avl(); 336 find_avl(); 337 walk_forwards(); 338 walk_backwards(); 339 inttree_inspect(); 340 remove_nodes(); 341 inttree_inspect(); 342 nearest_nodes(); 343 insert_avl(); 344 inttree_inspect(); 345 swap_avl(); 346 cleanup(); 347 return (0); 348 } 349 350 INTERFACE STABILITY 351 Committed 352 353 MT-Level 354 See Locking. 355 356 SEE ALSO 357 Intro(3), pthread_atfork(3C) 358 359 avl_add(3AVL), avl_create(3AVL), avl_destroy(3AVL), 360 avl_destroy_nodes(3AVL), avl_find(3AVL), avl_first(3AVL), 361 avl_insert(3AVL), avl_insert_here(3AVL), avl_is_empty(3AVL), 362 avl_last(3AVL), avl_nearest(3AVL), avl_numnodes(3AVL), avl_remove(3AVL), 363 avl_swap(3AVL), 364 365 Adel'son-Vel'skiy, G. M. and Landis, Ye. M., An Algorithm for the 366 Organization of Information, No. 2, Vol. 16, 263-266, Deklady Akademii 367 Nauk, USSR, Moscow, 1962. 368 369 illumos December 4, 2015 illumos