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 */
|