942 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			942 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** <plaintext>
 | |
| *
 | |
| * 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 */
 |