Files
argyllcms/numlib/aatree.c
2026-08-20 20:28:40 +01:00

432 lines
9.9 KiB
C

/*
Andersson binary balanced tree library
> Created (Julienne Walker): September 10, 2005
> Corrections (James Bucanek): April 10, 2006
1) Typo in jsw_aerase:
up != 0 should be top != 0
2) Bug in jsw_aerase:
skew ( path[top] ) should be skew ( up )
split ( path[top] ) should be split ( up )
3) Bug in skew and split macros:
Condition should test for nil
4) Bug in jsw_aerase:
Search for successor should save the path
> Fixed duplicate handling (Graeme W. Gill): August 16, 2009
*/
#include "aatree.h"
#ifdef __cplusplus
#include <cstdlib>
using std::malloc;
using std::free;
using std::size_t;
#else
#include <stdlib.h>
#endif
#ifndef HEIGHT_LIMIT
#define HEIGHT_LIMIT 64 /* Tallest allowable tree */
#endif
typedef struct aat_anode {
int level; /* Horizontal level for balance */
void *data; /* User-defined content */
struct aat_anode *link[2]; /* Left (0) and right (1) links */
} aat_anode_t;
struct aat_atree {
aat_anode_t *root; /* Top of the tree */
aat_anode_t *nil; /* End of tree sentinel */
cmp_f cmp; /* Compare two items */
size_t size; /* Number of items (user-defined) */
};
struct aat_atrav {
aat_atree_t *tree; /* Paired tree */
aat_anode_t *it; /* Current node */
aat_anode_t *path[HEIGHT_LIMIT]; /* Traversal path */
size_t top; /* Top of stack */
};
/* Remove left horizontal links */
#define skew(t) do { \
if ( t->link[0]->level == t->level && t->level != 0 ) { \
aat_anode_t *save = t->link[0]; \
t->link[0] = save->link[1]; \
save->link[1] = t; \
t = save; \
} \
} while(0)
/* Remove consecutive horizontal links */
#define split(t) do { \
if ( t->link[1]->link[1]->level == t->level && t->level != 0 ) { \
aat_anode_t *save = t->link[1]; \
t->link[1] = save->link[0]; \
save->link[0] = t; \
t = save; \
++t->level; \
} \
} while(0)
/* Version of cmp that ensure there are no duplicates */
/* Set res to comparison result */
#define AAT_CMP( res, tree, p1, p2) \
{ \
if ((res = tree->cmp ( p1, p2 )) == 0) { \
if (p1 < p2) \
res = -1; \
else if (p1 > p2) \
res = 1; \
} \
}
/* Create a new node that points to our data */
/* Return tree-nil if malloc failed. */
static aat_anode_t *new_node ( aat_atree_t *tree, void *data )
{
aat_anode_t *rn = (aat_anode_t *)malloc ( sizeof *rn );
if ( rn == NULL )
return tree->nil;
rn->level = 1;
rn->data = data;
rn->link[0] = rn->link[1] = tree->nil;
return rn;
}
/* Create a new empty tree */
aat_atree_t *aat_anew ( cmp_f cmp )
{
aat_atree_t *rt = (aat_atree_t *)malloc ( sizeof *rt );
if ( rt == NULL )
return NULL;
/* Initialize sentinel */
rt->nil = (aat_anode_t *)malloc ( sizeof *rt->nil );
if ( rt->nil == NULL ) {
free ( rt );
return NULL;
}
rt->nil->data = NULL; /* Simplifies some ops */
rt->nil->level = 0;
rt->nil->link[0] = rt->nil->link[1] = rt->nil;
/* Initialize tree */
rt->root = rt->nil;
rt->cmp = cmp;
rt->size = 0;
return rt;
}
/* Delete the whole tree (but not the data) */
void aat_adelete ( aat_atree_t *tree )
{
aat_anode_t *it = tree->root;
aat_anode_t *save;
/* Destruction by rotation */
while ( it != tree->nil ) {
if ( it->link[0] == tree->nil ) {
/* Remove node */
save = it->link[1];
free ( it );
}
else {
/* Rotate right */
save = it->link[0];
it->link[0] = save->link[1];
save->link[1] = it;
}
it = save;
}
/* Finalize destruction */
free ( tree->nil );
free ( tree );
}
/* Locate a entry based on the data's value */
void *aat_afind ( aat_atree_t *tree, void *data )
{
aat_anode_t *it = tree->root;
while ( it != tree->nil ) {
int cmp = tree->cmp ( it->data, data );
if ( cmp == 0 )
break;
it = it->link[cmp < 0];
}
/* nil->data == NULL */
return it->data;
}
/* Insert an entry based on the data's value. */
/* Take a copy of the pointer to the entry. */
/* Return 0 if malloc failed */
int aat_ainsert ( aat_atree_t *tree, void *data )
{
if ( tree->root == tree->nil ) {
/* Empty tree case */
tree->root = new_node ( tree, data );
if ( tree->root == tree->nil )
return 0;
}
else {
aat_anode_t *it = tree->root;
aat_anode_t *path[HEIGHT_LIMIT];
int top = 0, dir;
/* Find a spot and save the path */
for ( ; ; ) {
int cmp;
path[top++] = it;
AAT_CMP(cmp, tree, it->data, data);
dir = cmp < 0;
if ( it->link[dir] == tree->nil )
break;
it = it->link[dir];
}
/* Create a new item */
it->link[dir] = new_node ( tree, data );
if ( it->link[dir] == tree->nil )
return 0;
/* Walk back and rebalance */
while ( --top >= 0 ) {
/* Which child? */
if ( top != 0 )
dir = path[top - 1]->link[1] == path[top];
skew ( path[top] );
split ( path[top] );
/* Fix the parent */
if ( top != 0 )
path[top - 1]->link[dir] = path[top];
else
tree->root = path[top];
}
}
++tree->size;
return 1;
}
/* Delete a given entry by locating it by */
/* its value, then removing it from the tree. */
/* The item itself isn't deleted. */
/* Return 0 if the item wasn't found */
int aat_aerase ( aat_atree_t *tree, void *data )
{
if ( tree->root == tree->nil )
return 0;
else {
aat_anode_t *it = tree->root;
aat_anode_t *path[HEIGHT_LIMIT];
int top = 0, dir, cmp;
/* Find node to remove and save path */
for ( ; ; ) {
path[top++] = it;
if ( it == tree->nil )
return 0;
AAT_CMP(cmp, tree, it->data, data);
if ( cmp == 0 )
break;
dir = cmp < 0;
it = it->link[dir];
}
/* Remove the found node */
if ( it->link[0] == tree->nil
|| it->link[1] == tree->nil )
{
/* Single child case */
int dir2 = it->link[0] == tree->nil;
/* Unlink the item */
if ( --top != 0 )
path[top - 1]->link[dir] = it->link[dir2];
else
tree->root = it->link[1];
free ( it );
}
else {
/* Two child case */
aat_anode_t *heir = it->link[1];
aat_anode_t *prev = it;
while ( heir->link[0] != tree->nil ) {
path[top++] = prev = heir;
heir = heir->link[0];
}
/*
Order is important!
(free item, replace item, free heir)
*/
it->data = heir->data;
prev->link[prev == it] = heir->link[1];
free ( heir );
}
/* Walk back up and rebalance */
while ( --top >= 0 ) {
aat_anode_t *up = path[top];
if ( top != 0 )
dir = path[top - 1]->link[1] == up;
/* Rebalance (aka. black magic) */
if ( up->link[0]->level < up->level - 1
|| up->link[1]->level < up->level - 1 )
{
if ( up->link[1]->level > --up->level )
up->link[1]->level = up->level;
/* Order is important! */
skew ( up );
skew ( up->link[1] );
skew ( up->link[1]->link[1] );
split ( up );
split ( up->link[1] );
}
/* Fix the parent */
if ( top != 0 )
path[top - 1]->link[dir] = up;
else
tree->root = up;
}
}
--tree->size;
return 1;
}
/* Return the current number of items */
size_t aat_asize ( aat_atree_t *tree )
{
return tree->size;
}
/* Return a traversal object */
aat_atrav_t *aat_atnew ( void )
{
return malloc ( sizeof ( aat_atrav_t ) );
}
/* Destroy a traversal object */
void aat_atdelete ( aat_atrav_t *trav )
{
free ( trav );
}
/*
First step in traversal,
handles min and max
*/
static void *start ( aat_atrav_t *trav,
aat_atree_t *tree, int dir )
{
trav->tree = tree;
trav->it = tree->root;
trav->top = 0;
/* Build a path to work with */
if ( trav->it != tree->nil ) {
while ( trav->it->link[dir] != tree->nil ) {
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[dir];
}
}
/* Could be nil, but nil->data == NULL */
return trav->it->data;
}
/*
Subsequent traversal steps,
handles ascending and descending
*/
static void *move ( aat_atrav_t *trav, int dir )
{
aat_anode_t *nil = trav->tree->nil;
if ( trav->it->link[dir] != nil ) {
/* Continue down this branch */
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[dir];
while ( trav->it->link[!dir] != nil ) {
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[!dir];
}
}
else {
/* Move to the next branch */
aat_anode_t *last;
do {
if ( trav->top == 0 ) {
trav->it = nil;
break;
}
last = trav->it;
trav->it = trav->path[--trav->top];
} while ( last == trav->it->link[dir] );
}
/* Could be nil, but nil->data == NULL */
return trav->it->data;
}
/* Return first item */
void *aat_atfirst ( aat_atrav_t *trav, aat_atree_t *tree )
{
return start ( trav, tree, 0 ); /* Min value */
}
/* Return last item */
void *aat_atlast ( aat_atrav_t *trav, aat_atree_t *tree )
{
return start ( trav, tree, 1 ); /* Max value */
}
/* Move to next */
void *aat_atnext ( aat_atrav_t *trav )
{
return move ( trav, 1 ); /* Toward larger items */
}
/* Move to previous */
void *aat_atprev ( aat_atrav_t *trav )
{
return move ( trav, 0 ); /* Toward smaller items */
}