LIBAVL(3LIB) | Interface Libraries | LIBAVL(3LIB) |
libavl
— generic
self-balancing binary search tree library
AVL Tree Library (libavl, -lavl)
#include <sys/avl.h>
The libavl
library provides a generic
implementation of AVL trees, a form of self-balancing binary tree. The
interfaces provided allow for an efficient way of implementing an ordered
set of data structures and, due to its embeddable nature, allow for a single
instance of a data structure to belong to multiple AVL trees.
Each AVL tree contains entries of a single type of
data structure. Rather than allocating memory for pointers for those data
structures, the storage for the tree is embedded into the data structures by
declaring a member of type avl_node_t. When an AVL
tree is created, through the use of
avl_create
(),
it encodes the size of the data structure, the offset of the data structure,
and a comparator function which is used to compare two instances of a data
structure. A data structure may be a member of multiple AVL trees by
creating AVL trees which use different offsets (different members) into the
data structure.
AVL trees support both look up of an arbitrary item and ordered iteration over the contents of the entire tree. In addition, from any node, you can find the previous and next entries in the tree, if they exist. In addition, AVL trees support arbitrary insertion and deletion.
AVL trees are often used in place of linked lists. Compared to the standard, intrusive, doubly linked list, it has the following performance characteristics:
Note, insertions into an AVL tree always result in an ordered tree. Insertions into a linked list do not guarantee order. If order is required, then the time to do the insertion into a linked list will depend on the time of the search algorithm being employed to find the place to insert at.
In general, AVL trees are a good alternative for linked lists when order or lookup speed is important and a reasonable number of items will be present.
The shared object libavl.so.1 provides the public interfaces defined below. See Intro(3) for additional information on shared object interfaces. Individual functions are documented in their own manual pages.
In addition, the library defines C pre-processor macros which are defined below and documented in their own manual pages.
AVL_NEXT | AVL_PREV |
The libavl
library defines the following
types:
Type used for the root of the AVL tree. Consumers define one of these for each of the different trees that they want to have.
Type used as the data node for an AVL tree. One of these is embedded in each data structure that is the member of an AVL tree.
Type used to locate a position in a tree. This is used with avl_find(3AVL) and avl_insert(3AVL).
The libavl
library provides no locking.
Callers that are using the same AVL tree from multiple threads need to
provide their own synchronization. If only one thread is ever accessing or
modifying the AVL tree, then there are no synchronization concerns. If
multiple AVL trees exist, then they may all be used simultaneously; however,
they are subject to the same rules around simultaneous access from a single
thread.
All routines are both
Fork-safe and
Async-Signal-Safe.
Callers may call functions in libavl
from a signal
handler and libavl
calls are all safe in face of
fork(2); however, if callers have their
own locks, then they must make sure that they are accounted for by the use
of routines such as
pthread_atfork(3C).
The following code shows examples of exercising all of the
functionality that is present in libavl
. It can be
compiled by using a C compiler and linking against
libavl
. For example, given a file named avl.c, with
gcc, one would run:
$ gcc -Wall -o avl avl.c -lavl
/* * Example of using AVL Trees */ #include <sys/avl.h> #include <assert.h> #include <stddef.h> #include <stdlib.h> #include <stdio.h> static avl_tree_t inttree; /* * The structure that we're storing in an AVL tree. */ typedef struct intnode { int in_val; avl_node_t in_avl; } intnode_t; static int intnode_comparator(const void *l, const void *r) { const intnode_t *li = l; const intnode_t *ri = r; if (li->in_val > ri->in_val) return (1); if (li->in_val < ri->in_val) return (-1); return (0); } /* * Create an AVL Tree */ static void create_avl(void) { avl_create(&inttree, intnode_comparator, sizeof (intnode_t), offsetof(intnode_t, in_avl)); } /* * Add entries to the tree with the avl_add function. */ static void fill_avl(void) { int i; intnode_t *inp; for (i = 0; i < 20; i++) { inp = malloc(sizeof (intnode_t)); assert(inp != NULL); inp->in_val = i; avl_add(&inttree, inp); } } /* * Find entries in the AVL tree. Note, we create an intnode_t on the * stack that we use to look this up. */ static void find_avl(void) { int i; intnode_t lookup, *inp; for (i = 10; i < 30; i++) { lookup.in_val = i; inp = avl_find(&inttree, &lookup, NULL); if (inp == NULL) { printf("Entry %d is not in the tree\n", i); } else { printf("Entry %d is in the tree\n", inp->in_val); } } } /* * Walk the tree forwards */ static void walk_forwards(void) { intnode_t *inp; for (inp = avl_first(&inttree); inp != NULL; inp = AVL_NEXT(&inttree, inp)) { printf("Found entry %d\n", inp->in_val); } } /* * Walk the tree backwards. */ static void walk_backwards(void) { intnode_t *inp; for (inp = avl_last(&inttree); inp != NULL; inp = AVL_PREV(&inttree, inp)) { printf("Found entry %d\n", inp->in_val); } } /* * Determine the number of nodes in the tree and if it is empty or * not. */ static void inttree_inspect(void) { printf("The tree is %s, there are %ld nodes in it\n", avl_is_empty(&inttree) == B_TRUE ? "empty" : "not empty", avl_numnodes(&inttree)); } /* * Use avl_remove to remove entries from the tree. */ static void remove_nodes(void) { int i; intnode_t lookup, *inp; for (i = 0; i < 20; i+= 4) { lookup.in_val = i; inp = avl_find(&inttree, &lookup, NULL); if (inp != NULL) avl_remove(&inttree, inp); } } /* * Find the nearest nodes in the tree. */ static void nearest_nodes(void) { intnode_t lookup, *inp; avl_index_t where; lookup.in_val = 12; if (avl_find(&inttree, &lookup, &where) != NULL) abort(); inp = avl_nearest(&inttree, where, AVL_BEFORE); assert(inp != NULL); printf("closest node before 12 is: %d\n", inp->in_val); inp = avl_nearest(&inttree, where, AVL_AFTER); assert(inp != NULL); printf("closest node after 12 is: %d\n", inp->in_val); } static void insert_avl(void) { intnode_t lookup, *inp; avl_index_t where; lookup.in_val = 12; if (avl_find(&inttree, &lookup, &where) != NULL) abort(); inp = malloc(sizeof (intnode_t)); assert(inp != NULL); avl_insert(&inttree, inp, where); } static void swap_avl(void) { avl_tree_t swap; avl_create(&swap, intnode_comparator, sizeof (intnode_t), offsetof(intnode_t, in_avl)); avl_swap(&inttree, &swap); inttree_inspect(); avl_swap(&inttree, &swap); inttree_inspect(); } static void update_avl(void) { intnode_t lookup, *inp; avl_index_t where; lookup.in_val = 9; inp = avl_find(&inttree, &lookup, &where); assert(inp != NULL); inp->in_val = 25; avl_update(&inttree, inp); } /* * Remove all remaining nodes in the tree. We first use * avl_destroy_nodes to empty the tree, then avl_destroy to finish. */ static void cleanup(void) { intnode_t *inp; void *c = NULL; while ((inp = avl_destroy_nodes(&inttree, &c)) != NULL) { free(inp); } avl_destroy(&inttree); } int main(void) { create_avl(); inttree_inspect(); fill_avl(); find_avl(); walk_forwards(); walk_backwards(); inttree_inspect(); remove_nodes(); inttree_inspect(); nearest_nodes(); insert_avl(); inttree_inspect(); swap_avl(); update_avl(); cleanup(); return (0); }
See Locking.
avl_add(3AVL), avl_create(3AVL), avl_destroy(3AVL), avl_destroy_nodes(3AVL), avl_find(3AVL), avl_first(3AVL), avl_insert(3AVL), avl_insert_here(3AVL), avl_is_empty(3AVL), avl_last(3AVL), avl_nearest(3AVL), avl_numnodes(3AVL), avl_remove(3AVL), avl_swap(3AVL), avl_update(3AVL), avl_update_gt(3AVL), avl_update_lt(3AVL)
Adel'son-Vel'skiy, G. M. and Landis, Ye. M., An Algorithm for the Organization of Information, No. 2, Vol. 16, 263-266, Deklady Akademii Nauk, USSR, Moscow, 1962.
January 27, 2024 | OmniOS |