mirror of https://github.com/mabam/CAP.git
317 lines
8.2 KiB
C
317 lines
8.2 KiB
C
/*
|
|
* $Author: djh $ $Date: 91/02/15 21:05:43 $
|
|
* $Header: afpavl.c,v 2.1 91/02/15 21:05:43 djh Rel $
|
|
* $Revision: 2.1 $
|
|
*/
|
|
|
|
/*
|
|
* afpavl.c - Appletalk Filing Protocol AVL Tree Management
|
|
*
|
|
* AppleTalk package for UNIX (4.2 BSD).
|
|
*
|
|
* Copyright (c) 1986, 1987 by The Trustees of Columbia University in the
|
|
* City of New York.
|
|
*
|
|
*
|
|
* Edit History:
|
|
*
|
|
* Mar 30, 1987 Schilit Created, based on older versions
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include "afpavl.h"
|
|
|
|
/*
|
|
* The compare routines for the following MUST be used as:
|
|
* compare(userdata, node->userdata)
|
|
* This is because some of the callers may want to "cheat" on copying
|
|
* data and only send the key as the userdata. Since AVLInsert returns
|
|
* the AVLNode, the user data there can be modified after the fact
|
|
* if the userdata passed is not quite what we wanted (was only a search
|
|
* key)
|
|
*/
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
AVLNode *
|
|
AVLLookup(r,udata,compar)
|
|
AVLNodePtr r; /* root, start of search */
|
|
AVLUData *udata; /* check for this user data */
|
|
int (*compar)(); /* comparison routine */
|
|
{
|
|
int cmp;
|
|
|
|
while (r != NILAVL) { /* until we runnout... */
|
|
cmp = (*compar)(udata,r->b_udata); /* compare the keys */
|
|
if (cmp < 0) /* key < r */
|
|
r = r->b_l; /* move left */
|
|
else if (cmp > 0) /* key > r */
|
|
r = r->b_r; /* move right */
|
|
else /* key = r */
|
|
return(r); /* return the node */
|
|
}
|
|
return(NULL); /* not found, return null */
|
|
}
|
|
|
|
AVLNode *
|
|
AVLNew(udata)
|
|
AVLUData *udata;
|
|
{
|
|
AVLNode *new;
|
|
|
|
new = (AVLNode *) malloc(sizeof(AVLNode));
|
|
new->b_bf = 0; /* init balance factor */
|
|
new->b_l = /* and left, right pointer */
|
|
new->b_r = NILAVL; /* fields in new node */
|
|
new->b_udata = udata; /* install user data */
|
|
return(new); /* and return new ptr */
|
|
}
|
|
|
|
/*
|
|
* int AVLInsert(AVLNode **root, AVLUData *udata, int (*compar)())
|
|
*
|
|
* Create a new node with the user's data attached to it (AVLUData).
|
|
* Use the compar routine to decide where this entry goes in the
|
|
* tree. Always returns user data of node inserted or found.
|
|
*
|
|
* Rewrote to match closer to Knuth version: cf. Searching and Sorting,
|
|
* The Art of Computer Programming, Volume 3, pp. 455-457.
|
|
*
|
|
*/
|
|
|
|
AVLNode *
|
|
AVLInsert(root,udata,compar)
|
|
AVLNode **root; /* handle on root node */
|
|
AVLUData *udata; /* user data for new node */
|
|
int (*compar)(); /* comparison routine */
|
|
{
|
|
AVLNodePtr t,s,p,q,r;
|
|
int bf,cmp; /* balance factor */
|
|
|
|
if ((p = *root) == NILAVL) { /* check for empty tree */
|
|
*root = AVLNew(udata); /* if so then insert and */
|
|
return(*root); /* return... */
|
|
}
|
|
|
|
/*
|
|
* p move down the tree to locate the insertion
|
|
* point for new.
|
|
*
|
|
* s is the node to rebalance around: the closest node to new which
|
|
* has a balance factor different from zero. t is the father of s.
|
|
* If t stays null then we want to rebalance around "root"
|
|
*/
|
|
|
|
/* A1 */
|
|
t = NILAVL;
|
|
s = p;
|
|
|
|
do {
|
|
/* A2 */
|
|
if ((cmp = (*compar)(udata,p->b_udata)) == 0)
|
|
return(p);
|
|
if (cmp < 0) { /* comparison base */
|
|
/* A3 */
|
|
if ((q = p->b_l) == NILAVL) { /* go left unless NIL */
|
|
p->b_l = q = AVLNew(udata); /* if nil, then insert */
|
|
break; /* and goto rest of code */
|
|
}
|
|
} else { /* cmp > 0 */
|
|
/* A4 */
|
|
if ((q = p->b_r) == NILAVL) { /* go right unless NIL */
|
|
p->b_r = q = AVLNew(udata);
|
|
break;
|
|
}
|
|
}
|
|
if (q->b_bf != 0) {
|
|
t = p; /* new rebalance point */
|
|
s = q;
|
|
}
|
|
p = q;
|
|
} while (1);
|
|
|
|
/* A6 */
|
|
/* adjust the balance factors of nodes on path from next of s to q */
|
|
|
|
/* remember bf at balance point */
|
|
bf = ((*compar)(udata,s->b_udata) < 0) ? -1 : 1;
|
|
if (bf < 0) /* take the first step... */
|
|
r = p = s->b_l; /* to get past the balance point */
|
|
else
|
|
r = p = s->b_r;
|
|
|
|
/* follow p down to q and set each balance factor to 1 or -1.
|
|
* no addition is required since balance factors of all nodes after
|
|
* s are zero (s is the closest non zero node).
|
|
*/
|
|
|
|
while (p != q) { /* follow the path from p to new */
|
|
/* set balance factors for nodes */
|
|
cmp = (*compar)(udata,p->b_udata);
|
|
#ifdef DEBUG
|
|
if (cmp == 0) { /* p == q !! */
|
|
fprintf(stderr, "Can't happen p == q in AVLInsert A6\n");
|
|
exit(9999);
|
|
}
|
|
#endif
|
|
p->b_bf = cmp < 0 ? -1 : 1; /* adjust */
|
|
p = cmp < 0 ? p->b_l : p->b_r; /* and follow correct path */
|
|
}
|
|
|
|
/* A7 */
|
|
/* check if the tree is unbalanced */
|
|
|
|
if (s->b_bf == 0) { /* at balance point check bf */
|
|
s->b_bf = bf; /* was balanced now only 1 off */
|
|
return(q); /* tree is ok (return(q)) */
|
|
}
|
|
|
|
if (s->b_bf == -bf) { /* at balance point did we improve? */
|
|
s->b_bf = 0; /* yes, set balance factor to 0 */
|
|
return(q); /* and return (q - new node ) */
|
|
}
|
|
|
|
/* balance factor went to -2 or +2, rebalance the tree */
|
|
if (r->b_bf == bf) { /* single rotation */
|
|
p = r;
|
|
if (bf < 0) {
|
|
s->b_l = r->b_r;
|
|
r->b_r = s;
|
|
} else {
|
|
s->b_r = r->b_l;
|
|
r->b_l = s;
|
|
}
|
|
s->b_bf = r->b_bf = 0; /* subtree is now balanced */
|
|
} else {
|
|
/* double rotation */
|
|
if (bf < 0) {
|
|
p = r->b_r;
|
|
r->b_r = p->b_l;
|
|
p->b_l = r;
|
|
s->b_l = p->b_r;
|
|
p->b_r = s;
|
|
} else {
|
|
p = r->b_l;
|
|
r->b_l = p->b_r;
|
|
p->b_r = r;
|
|
s->b_r = p->b_l;
|
|
p->b_l = s;
|
|
}
|
|
if (p->b_bf == 0)
|
|
s->b_bf = r->b_bf = 0;
|
|
else {
|
|
if (p->b_bf == bf) { /* b(p) = a */
|
|
s->b_bf = -bf;
|
|
r->b_bf = 0;
|
|
} else { /* b(p) = -a */
|
|
s->b_bf = 0;
|
|
r->b_bf = bf;
|
|
}
|
|
}
|
|
p->b_bf = 0;
|
|
}
|
|
|
|
/* A10 */
|
|
/* Finishing touch: s points to the new root and t points to the
|
|
* father of the old root of the rebalanced subtree. Make t point
|
|
* to the head of the balanced subtree.
|
|
*/
|
|
|
|
if (t == NILAVL) /* rebalance around the root node? */
|
|
*root = p; /* yes, set a new root */
|
|
else {
|
|
if (s == t->b_r) /* did we rebalance on right? */
|
|
t->b_r = p; /* yes, then set new subtree there */
|
|
else
|
|
t->b_l = p; /* else rebalanced on left */
|
|
}
|
|
return(q); /* return new node */
|
|
}
|
|
|
|
#ifdef notdef
|
|
AVLNode *
|
|
AVLLookN(r,udata,n,comp)
|
|
AVLNode *r;
|
|
AVLUData *udata;
|
|
int *n;
|
|
int (*comp)();
|
|
{
|
|
AVLNode *s;
|
|
int cmp;
|
|
|
|
while (r != NILAVL) { /* until we runnout... */
|
|
cmp = (*comp)(udata,r->b_udata); /* compare the keys */
|
|
if (cmp < 0) /* key < r */
|
|
r = r->b_l; /* move left */
|
|
else if (cmp > 0) /* key > r */
|
|
r = r->b_r; /* move right */
|
|
else /* key = r */
|
|
break; /* found a match */
|
|
}
|
|
if (r == NILAVL) /* check if runnout */
|
|
return(NILAVL); /* yes... then not found */
|
|
if (--(*n) <= 0) /* no... found something, decr n */
|
|
return(r); /* it was the one we wanted */
|
|
s = AVLLookN(r->b_l,udata,n,comp); /* else check left subtree */
|
|
if (s != NILAVL) /* found it there? */
|
|
return(s); /* return the node */
|
|
return(AVLLookN(r->b_r,udata,n,comp)); /* else check right subtree */
|
|
}
|
|
|
|
/*
|
|
* AVLLookupNth(AVLNode *root, AVLUData *udata, int n, int (*compare)())
|
|
*
|
|
* Call the specified comparison routine to locate the n'th node
|
|
* that matches user data. The primary key used by compare must
|
|
* match the primary key used by AVLInsert.
|
|
*
|
|
*/
|
|
|
|
AVLUData *
|
|
AVLLookupNth(r,udata,n,comp)
|
|
AVLNodePtr r;
|
|
AVLUData *udata;
|
|
int n;
|
|
int (*comp)();
|
|
{
|
|
AVLNode *s,*f; /* subtree */
|
|
int cmp;
|
|
|
|
f = NILAVL;
|
|
while (r != NILAVL && f == NILAVL) { /* locate first matching */
|
|
cmp = (*comp)(udata,r->b_udata); /* compare the keys */
|
|
if (cmp < 0) /* key < r */
|
|
r = r->b_l; /* move left */
|
|
else if (cmp > 0) /* key > r */
|
|
r = r->b_r; /* move right */
|
|
else /* key = r */
|
|
f = r; /* found it set f exits loop */
|
|
}
|
|
if (f == NILAVL)
|
|
return((AVLUData *) 0); /* nothing found */
|
|
s = AVLLookN(f,udata,&n,comp); /* call locator routine */
|
|
if (s != NILAVL) /* did we find it? */
|
|
return(s->b_udata); /* yes, return user data */
|
|
return((AVLUData *) 0); /* return failure */
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
AVLMapTree(root,pnode,uhdl)
|
|
AVLNodePtr root;
|
|
void (*pnode)();
|
|
char *uhdl;
|
|
{
|
|
if (root == NILAVL)
|
|
return;
|
|
(*pnode)(root->b_udata,uhdl); /* call with udata */
|
|
if (root->b_l != NILAVL)
|
|
AVLMapTree(root->b_l,pnode,uhdl); /* do left nodes */
|
|
if (root->b_r != NILAVL)
|
|
AVLMapTree(root->b_r,pnode,uhdl); /* do right nodes */
|
|
}
|