/** * * avl.c -- C source file for avl trees. Contains the auxillary routines * and defines for the avl tree functions and user interface and * includes all the necessary public and private routines * * Created 03/01/89 by Brad Appleton * * ^{Mods:* } * * Fri Jul 14 13:53:42 1989, Rev 1.0, brad(0165) * _______________________________________________________________________________ LICENSE ======= This software is not subject to any license of the American Telephone and Telegraph Company or of the Regents of the University of California. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it freely, subject to the following restrictions: 1. Neither the authors of the software nor their employers (including any of the employers' subsidiaries and subdivisions) are responsible for maintaining & supporting this software or for any consequences resulting from the use of this software, no matter how awful, even if they arise from flaws in the software (see LIABILITY). 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation. 4. This notice may not be removed or altered. _______________________________________________________________________________ NO WARRANTY =========== Because this software is licensed free of charge, we (the authors and/or distributors of this software) provide absolutely no warranty, to the extent permitted by applicable state law. Except when otherwise stated in writing, the authors and distributors of this software and/or other parties provide this program "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of this software lies with the recipients and/or users of this software and not with any of the authors and/or distributors of this software, nor with any of their employers, including any of the employers' subsidiaries and subdivisions. Should the program prove defective, the recipients/users of this software assume the cost of all necessary servicing, repair, or correction. _______________________________________________________________________________ LIABILITY ========= In no event unless required by applicable law will the authors and/or distributors of this software, their employers, and any subsidiaries and/or subdivisions of their employers, and/or any other party who may redistribute this software as permitted in the LICENSE section of this notice, be liable to the recipients/users of this software for damages including any lost profits, lost monies, or other special, incidental, or consequential damages arising out of the use or inabilty to use (including but not limited to loss of data or data being rendered inaccurate or lossed sustained by third parties or a failure of the software to operate with any other software or hardware) this software, even if you have been advised of the possibility of such damages, or for any claim by any other party. _______________________________________________________________________________ Heavily modified by Jan Wielemaker, wielemak@science.uva.nl. Brief summary: - Added prototypes and reformatted the sources. - Added includes - Swapped various arguments - Changes some of the interfaces - Added enumeration interface **/ /* some common #defines used throughout most of my files */ #define PUBLIC /* default */ #define FALSE 0 #define TRUE !FALSE /* some defines for debugging purposes */ #ifdef NDEBUG #define DBG(x) /* x */ #else #define DBG(x) x #endif #define NEXTERN /* dont include "extern" declarations from header files */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "avl.h" /* public types for avl trees */ #ifdef __WINDOWS__ #define inline __inline #endif /* MIN and MAX macros (used for rebalancing) */ #undef MIN #undef MAX #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /************************************************************************ * Auxillary functions * * routines to allocate/de-allocate an AVL node, * and determine the type of an AVL node. ************************************************************************/ /* ckalloc(size) -- allocate space; check for success */ static void * ckalloc(size_t size) { void *ptr; if ( !(ptr = malloc(size)) ) { fprintf(stderr, "Unable to allocate storage."); exit(1); } return ptr; } /* ckalloc */ #define sizeofnode(size) ((size_t)&((AVLtree)0)->data[0] + size) /* * new_node() -- get space for a new node and its data; * return the address of the new node */ static AVLtree new_node(AVL_TREE tree, void *data) { AVLtree root; if ( tree->alloc ) root = (*tree->alloc)(tree->client_data, sizeofnode(tree->isize)); else root = ckalloc(sizeofnode(tree->isize)); memcpy(root->data, data, tree->isize); root->bal = BALANCED; root->subtree[LEFT] = root->subtree[RIGHT] = NULL_TREE; return root; } /* new_node */ /* * free_node() -- free space for a node and its data! * reset the node pointer to NULL */ static void free_node(AVL_TREE tree, AVLtree *rootp) { AVLtree root = *rootp; if ( tree->destroy ) (*tree->destroy)(root->data); if ( tree->free ) (*tree->free)(tree->client_data, root, sizeofnode(tree->isize)); else free(root); *rootp = NULL_TREE; } /* free_node */ /* * node_type() -- determine the number of null pointers for a given * node in an AVL tree, Returns a value of type NODE * which is an enumeration type with the following values: * * IS_TREE -- both subtrees are non-empty * IS_LBRANCH -- left subtree is non-empty; right is empty * IS_RBRANCH -- right subtree is non-empty; left is empty * IS_LEAF -- both subtrees are empty * IS_NULL -- given tree is empty */ static NODE node_type(AVLtree tree) { if (tree == NULL_TREE) { return IS_NULL; } else if ((tree->subtree[LEFT] != NULL_TREE) && (tree->subtree[RIGHT] != NULL_TREE)) { return IS_TREE; } else if (tree->subtree[LEFT] != NULL_TREE) { return IS_LBRANCH; } else if (tree->subtree[RIGHT] != NULL_TREE) { return IS_RBRANCH; } else { return IS_LEAF; } } /* node_type */ /************************************************************************ * static functions for manipulating AVL trees * * This following defines a set of routines for creating, maintaining, and * manipulating AVL Trees as an Abtract Data Type. The routines in this * file that are accessible (through the avl tree user-interface) to other * files to allow other programmers to: * * Insert, Delete, and Find a given data item from a Tree. * * Delete and Find the minimal and Maximal items in a Tree. * * Walk through every node in a tree performing a giving operation. * * Walk through and free up space for every node in a tree while performing * a given operation on the data items as they are encountered. ************************************************************************/ /************************************************************************ * routines used to find the minimal and maximal elements * (nodes) of an AVL tree. ************************************************************************/ /* * avl_min() -- compare function used to find the minimal element in a tree */ static int avl_min(void *elt1, void *elt2, NODE nd_typ) { return (nd_typ == IS_RBRANCH || nd_typ == IS_LEAF) ? 0 /* left subtree is empty -- this is the minimum */ : -1; /* keep going left */ } /* avl_min */ /* * avl_max() -- compare function used to find the maximal element in a tree */ static int avl_max(void *elt1, void *elt2, NODE nd_typ) { return (nd_typ == IS_LBRANCH || nd_typ == IS_LEAF) ? 0 /* right subtree is empty -- this is the maximum */ : 1; /* keep going right */ } /* avl_max */ /************************************************************************ * Routines to perform rotations on AVL trees ************************************************************************/ /* * rotate_once() -- rotate a given node in the given direction * to restore the balance of a tree */ static short rotate_once(AVLtree * rootp, DIRECTION dir) { DIRECTION other_dir = OPPOSITE(dir); /* opposite direction to "dir" */ AVLtree old_root = *rootp; /* copy of original root of tree */ short ht_unchanged; /* true if height unchanged */ ht_unchanged = ((*rootp)->subtree[other_dir]->bal) ? FALSE : TRUE; /* assign new root */ *rootp = old_root->subtree[other_dir]; /* new-root exchanges it's "dir" subtree for it's parent */ old_root->subtree[other_dir] = (*rootp)->subtree[dir]; (*rootp)->subtree[dir] = old_root; /* update balances */ old_root->bal = -(dir == LEFT ? --((*rootp)->bal) : ++((*rootp)->bal)); return ht_unchanged; } /* rotate_once */ /* * rotate_twice() -- rotate a given node in the given direction * and then in the opposite direction * to restore the balance of a tree */ static void rotate_twice(AVLtree * rootp, DIRECTION dir) { DIRECTION other_dir = OPPOSITE(dir); AVLtree old_root = *rootp; AVLtree old_other_dir_subtree = (*rootp)->subtree[other_dir]; /* assign new root */ *rootp = old_root->subtree[other_dir]->subtree[dir]; /* new-root exchanges it's "dir" subtree for it's grandparent */ old_root->subtree[other_dir] = (*rootp)->subtree[dir]; (*rootp)->subtree[dir] = old_root; /* new-root exchanges it's "other-dir" subtree for it's parent */ old_other_dir_subtree->subtree[dir] = (*rootp)->subtree[other_dir]; (*rootp)->subtree[other_dir] = old_other_dir_subtree; /* update balances */ (*rootp)->subtree[LEFT]->bal = -MAX((*rootp)->bal, 0); (*rootp)->subtree[RIGHT]->bal = -MIN((*rootp)->bal, 0); (*rootp)->bal = 0; } /* rotate_twice */ /************************************************************************ * Rebalance an AVL tree ************************************************************************/ /* * balance() -- determines and performs the sequence of rotations needed * (if any) to restore the balance of a given tree. * * Returns 1 if tree height changed due to rotation; 0 otherwise */ static short balance(AVLtree * rootp) { short special_case = FALSE; if (LEFT_IMBALANCE(*rootp)) { /* need a right rotation */ if ((*rootp)->subtree[LEFT]->bal == RIGHT_HEAVY) { rotate_twice(rootp, RIGHT); /* double RL rotation needed */ } else { /* single RR rotation needed */ special_case = rotate_once(rootp, RIGHT); } } else if (RIGHT_IMBALANCE(*rootp)) { /* need a left rotation */ if ((*rootp)->subtree[RIGHT]->bal == LEFT_HEAVY) { rotate_twice(rootp, LEFT); /* double LR rotation needed */ } else { /* single LL rotation needed */ special_case = rotate_once(rootp, LEFT); } } else { return HEIGHT_UNCHANGED; /* no rotation occurred */ } return (special_case) ? HEIGHT_UNCHANGED : HEIGHT_CHANGED; } /* balance */ /************************************************************************ * Routines to: Find an item in an AVL tree * Insert an item into an AVL tree * Delete an item from an AVL tree ************************************************************************/ /* * avl_find() -- find an item in the given tree * * PARAMETERS: * data -- a pointer to the key to find * rootp -- a pointer to an AVL tree * compar -- name of a function to compare 2 data items */ static void * avl_find(void *data, AVLtree tree, int (*compar) (void *l, void *r, NODE)) { NODE nd_typ = node_type(tree); int cmp; while ((tree != NULL_TREE) && (cmp = (*compar)(data, tree->data, nd_typ))) { tree = tree->subtree[(cmp < 0) ? LEFT : RIGHT]; } return (tree == NULL_TREE) ? NULL : tree->data; } /* avl_find */ /* * avl_insert() -- insert an item into the given tree * * PARAMETERS: * data -- a pointer to a pointer to the data to add; * On exit, *data is NULL if insertion succeeded, * otherwise address of the duplicate key * rootp -- a pointer to an AVL tree * compar -- name of the function to compare 2 data items */ static short avl_insert(AVL_TREE tree, AVLtree *rootp, void **data) { short increase; int cmp; if (*rootp == NULL_TREE) { /* insert new node here */ *rootp = new_node(tree, *data); *data = NULL; /* set return value in data */ return HEIGHT_CHANGED; } cmp = (*tree->compar)(*data, (*rootp)->data, IS_NULL); if (cmp < 0) { /* insert into the left subtree */ increase = -avl_insert(tree, &((*rootp)->subtree[LEFT]), data); if (*data != NULL) return HEIGHT_UNCHANGED; } else if (cmp > 0) { /* insert into the right subtree */ increase = avl_insert(tree, &((*rootp)->subtree[RIGHT]), data); if (*data != NULL) return HEIGHT_UNCHANGED; } else { /* data already exists */ *data = (*rootp)->data; /* set return value in data */ return HEIGHT_UNCHANGED; } (*rootp)->bal += increase; /* update balance factor */ /************************************************************************ * re-balance if needed -- height of current tree increases only if its * subtree height increases and the current tree needs no rotation. ************************************************************************/ return (increase && (*rootp)->bal) ? (1 - balance(rootp)) : HEIGHT_UNCHANGED; } /* avl_insert */ static void memswap(void *p1, void *p2, size_t size) { char *s1 = p1; char *s2 = p2; char buf[256]; while(size > 0) { size_t bytes = (size > sizeof(buf) ? sizeof(buf) : size); memcpy(buf, s1, bytes); memcpy(s1, s2, bytes); memcpy(s2, buf, bytes); s1 += bytes; s2 += bytes; size -= bytes; } } /* * avl_delete() -- delete an item from the given tree * * PARAMETERS: * data -- a pointer to a pointer to the key to delete * On exit, *data points to the deleted data item * (or NULL if deletion failed). * rootp -- a pointer to an AVL tree * compar -- name of function to compare 2 data items */ static short avl_delete(AVL_TREE tree, AVLtree *rootp, void *data, int *found, int (*compar)(void*, void*, NODE)) { short decrease; int cmp; AVLtree old_root = *rootp; NODE nd_typ = node_type(*rootp); DIRECTION dir = (nd_typ == IS_LBRANCH) ? LEFT : RIGHT; if (*rootp == NULL_TREE) { /* data not found */ if ( found ) *found = FALSE; return HEIGHT_UNCHANGED; } cmp = (*compar)(data, (*rootp)->data, nd_typ); if (cmp < 0) { /* delete from left subtree */ decrease = -avl_delete(tree, &((*rootp)->subtree[LEFT]), data, found, compar); if ( found && *found == FALSE ) return HEIGHT_UNCHANGED; } else if (cmp > 0) { /* delete from right subtree */ decrease = avl_delete(tree, &((*rootp)->subtree[RIGHT]), data, found, compar); if ( found && *found == FALSE ) return HEIGHT_UNCHANGED; } else { /* cmp == 0 */ decrease = 0; /* JW: Silence compiler */ if ( found ) *found = TRUE; if ( data && data != (*rootp)->data ) { if ( found ) memcpy(data, (*rootp)->data, tree->isize); else memswap(data, (*rootp)->data, tree->isize); } /*********************************************************************** * At this point we know "cmp" is zero and "*rootp" points to * the node that we need to delete. There are three cases: * * 1) The node is a leaf. Remove it and return. * * 2) The node is a branch (has only 1 child). Make "*rootp" * (the pointer to this node) point to the child. * * 3) The node has two children. We swap data with the successor of * "*rootp" (the smallest item in its right subtree) and delete * the successor from the right subtree of "*rootp". The * identifier "decrease" should be reset if the subtree height * decreased due to the deletion of the successor of "rootp". ***********************************************************************/ switch (nd_typ) { /* what kind of node are we removing? */ case IS_LEAF: free_node(tree, rootp); /* free the leaf, its height */ return HEIGHT_CHANGED; /* changes from 1 to 0, return 1 */ case IS_RBRANCH: /* only child becomes new root */ case IS_LBRANCH: *rootp = (*rootp)->subtree[dir]; free_node(tree, &old_root); /* free the deleted node */ return HEIGHT_CHANGED; /* we just shortened the "dir" subtree */ case IS_TREE: decrease = avl_delete(tree, &((*rootp)->subtree[RIGHT]), (*rootp)->data, NULL, avl_min); break; case IS_NULL: /* JW: Silence compiler. Cannot happen */ assert(0); break; } /* switch */ } /* else */ (*rootp)->bal -= decrease; /* update balance factor */ /********************************************************************** * Rebalance if necessary -- the height of current tree changes if one * of two things happens: (1) a rotation was performed which changed * the height of the subtree (2) the subtree height decreased and now * matches the height of its other subtree (so the current tree now * has a zero balance when it previously did not). **********************************************************************/ if (decrease && (*rootp)->bal) { return balance(rootp); /* rebalance and see if height changed */ } else if (decrease && !(*rootp)->bal) { return HEIGHT_CHANGED; /* balanced because subtree decreased */ } else { return HEIGHT_UNCHANGED; } } /* avl_delete */ /** * Routines which Recursively Traverse an AVL TREE * * These routines may perform a particular action function upon each node * encountered. In these cases, "action" has the following definition: * * void action(data, order, node, level, bal) * void *data * VISIT order; * NODE node; * short bal; * int level; * * "data" is a pointer to the data field of an AVL node * "order" corresponds to whether this is the 1st, 2nd or 3rd time * that this node has been visited. * "node" indicates which children (if any) of the current node * are null. * "level" is the current level (or depth) in the tree of the * curent node. * "bal" is the balance factor of the current node. **/ /************************************************************************ * Walk an AVL tree, performing a given function at each node ************************************************************************/ /* * avl_walk -- traverse the given tree performing "action" * upon each data item encountered. * */ static void avl_walk(AVLtree tree, void (*action)(void *data, SIBLING_ORDER order, NODE type, int level, int balance), SIBLING_ORDER sibling_order, int level) { DIRECTION dir1 = (sibling_order == LEFT_TO_RIGHT) ? LEFT : RIGHT; DIRECTION dir2 = OPPOSITE(dir1); NODE node = node_type(tree); if ( tree && action ) { (*action) (tree->data, PREORDER, node, level, (int)tree->bal); if (tree->subtree[dir1] != NULL_TREE) { avl_walk(tree->subtree[dir1], action, sibling_order, level + 1); } (*action) (tree->data, INORDER, node, level, (int)tree->bal); if (tree->subtree[dir2] != NULL_TREE) { avl_walk(tree->subtree[dir2], action, sibling_order, level + 1); } (*action) (tree->data, POSTORDER, node, level, (int)tree->bal); } /* if non-empty tree */ } /* avl_walk */ /* * avl_free() -- free up space for all nodes in a given tree */ static void avl_free(AVL_TREE tree, AVLtree *rootp) { if ( *rootp ) { if ( (*rootp)->subtree[LEFT] != NULL_TREE ) avl_free(tree, &(*rootp)->subtree[LEFT]); if ( (*rootp)->subtree[RIGHT] != NULL_TREE ) avl_free(tree, &(*rootp)->subtree[RIGHT]); free_node(tree, rootp); } } /******************************* * ENUMERATION INTERFACE * *******************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - avlfindfirst() avlfindnext() avlfinddestroy() This interface allows for enumerating all elements in a key-range. avl_find_ge() finds the first node whose key is larger or equal to a given key. avlfindnext() returns the next node. avlfinddestroy() destroyes memory allocated for the enum (if any). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static inline AVLtree push_node(avl_enum *e, AVLtree node) { e->parents[e->current++] = node; return node; } static inline AVLtree pop_node(avl_enum *e) { if ( --e->current >= 0 ) return e->parents[e->current]; return NULL; } static inline AVLtree current_node(avl_enum *e) { if ( e->current >= 1 ) return e->parents[e->current-1]; return NULL; } void * avlfindfirst(AVL_TREE tree, void *data, avl_enum *e) { AVLtree node = tree->root; if ( !node ) return NULL; e->tree = tree; e->current = 0; for(;;) { int diff; if ( data ) diff = (*tree->compar)(data, node->data, IS_NULL); else diff = -1; /* search first node */ if ( diff < 0 ) { push_node(e, node); if ( node->subtree[LEFT] ) { node = node->subtree[LEFT]; } else { return node->data; /* key > target */ } } else if ( diff > 0 ) { if ( node->subtree[RIGHT] ) { node = node->subtree[RIGHT]; } else { if ( (node = current_node(e)) ) return node->data; return NULL; } } else { return push_node(e, node)->data; /* equal hit */ } } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Last pushed node is the node returned. All nodes to the left are considered `done'. We must return all nodes to the right, followed by the parent. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void * avlfindnext(avl_enum *e) { AVLtree n = pop_node(e); if ( n->subtree[RIGHT] ) { n = push_node(e, n->subtree[RIGHT]); while(n->subtree[LEFT]) n = push_node(e, n->subtree[LEFT]); return n->data; } n = current_node(e); return n ? n->data : NULL; } void avlfinddestroy(avl_enum *e) { } /********************************************************************** * * C-interface (public functions) for avl trees * * These are the functions that are visible to the user of the * AVL Tree Library. Mostly they just return or modify a * particular attribute, or Call a private functions with the * given parameters. * * Note that public routine names begin with "avl" whereas * private routine names that are called by public routines * begin with "avl_" (the underscore character is added). * * Each public routine must convert (cast) any argument of the * public type "AVL_TREE" to a pointer to on object of the * private type "AVLdescriptor" before passing the actual * AVL tree to any of the private routines. In this way, the * type "AVL_TREE" is implemented as an opaque type. * * An "AVLdescriptor" is merely a container for AVL-tree * objects which contains the pointer to the root of the * tree and the various attributes of the tree. * * The function types prototypes for the routines which follow * are declared in the include file "avl.h" * ***********************************************************************/ /* * avlinit() -- get space for an AVL descriptor for the given tree * structure and initialize its fields. */ PUBLIC AVL_TREE avlinit(AVL_TREE tree, void *cdata, size_t isize, int (*compare)(void *l, void*r, NODE type), void (*destroy)(void *data), void* (*alloc)(void *cdata, size_t bytes), void (*free)(void *cdata, void *data, size_t bytes)) { tree->root = NULL_TREE; tree->compar = compare; tree->destroy = destroy; tree->alloc = alloc; tree->free = free; tree->isize = (int)isize; tree->client_data = cdata; tree->count = 0; return (AVL_TREE) tree; } /* avlinit */ /* * avldispose() -- free up all space associated with the given tree structure. */ PUBLIC void avlfree(AVL_TREE tree) { avl_free(tree, &(tree->root)); } /* avldispose */ /* * avlwalk() -- traverse the given tree structure and perform the * given action on each data item in the tree. */ PUBLIC void avlwalk(AVL_TREE tree, void (*action)(void *data, SIBLING_ORDER order, NODE type, int level, int balance), SIBLING_ORDER sibling_order) { avl_walk(tree->root, action, sibling_order, 1); } /* avlwalk */ /* * avlcount() -- return the number of nodes in the given tree */ PUBLIC int avlcount(AVL_TREE tree) { return tree->count; } /* avlcount */ /* * avlins() -- insert the given item into the tree structure */ PUBLIC void * avlins(AVL_TREE tree, void *data) { avl_insert(tree, &tree->root, &data); if ( data == NULL ) tree->count++; return data; } /* avlins */ /* * avldel() -- delete the given item from the given tree structure */ PUBLIC int avldel(AVL_TREE tree, void *data) { int found; avl_delete(tree, &tree->root, data, &found, tree->compar); if ( found ) tree->count--; return found; } /* * avlfind() -- find the given item in the given tree structure * and return its address (NULL if not found). */ PUBLIC void * avlfind(AVL_TREE tree, void *data) { return avl_find(data, tree->root, tree->compar); } /* avlfind */ /* * avldelmin() -- delete the minimal item from the given tree structure */ PUBLIC int avldelmin(AVL_TREE tree, void *data) { int found; avl_delete(tree, &tree->root, data, &found, avl_min); if ( found ) tree->count--; return found; } /* avldelmin */ /* * avlfindmin() -- find the minimal item in the given tree structure * and return its address (NULL if not found). */ PUBLIC void * avlfindmin(AVL_TREE tree) { return avl_find(NULL, tree->root, avl_min); } /* avlfindmin */ /* * avldelmax() -- delete the maximal item from the given tree structure */ PUBLIC int avldelmax(AVL_TREE tree, void *data) { int found; avl_delete(tree, &tree->root, data, &found, avl_max); if ( found ) tree->count--; return found; } /* avldelmax */ /* * avlfindmax() -- find the maximal item in the given tree structure * and return its address (NULL if not found). */ PUBLIC void * avlfindmax(AVL_TREE tree) { return avl_find(NULL, tree->root, avl_max); } /* avlfindmax */