533 lines
12 KiB
C
533 lines
12 KiB
C
|
#ifndef LINT
|
||
|
static char RCSid[] = "$Id: tree.c,v 8.6 1997/09/26 17:56:11 halley Exp $";
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* tree - balanced binary tree library
|
||
|
*
|
||
|
* vix 05apr94 [removed vixie.h dependencies; cleaned up formatting, names]
|
||
|
* vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes]
|
||
|
* vix 23jun86 [added delete uar to add for replaced nodes]
|
||
|
* vix 20jun86 [added tree_delete per wirth a+ds (mod2 v.) p. 224]
|
||
|
* vix 06feb86 [added tree_mung()]
|
||
|
* vix 02feb86 [added tree balancing from wirth "a+ds=p" p. 220-221]
|
||
|
* vix 14dec85 [written]
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* This program text was created by Paul Vixie using examples from the book:
|
||
|
* "Algorithms & Data Structures," Niklaus Wirth, Prentice-Hall, 1986, ISBN
|
||
|
* 0-13-022005-1. Any errors in the conversion from Modula-2 to C are Paul
|
||
|
* Vixie's.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Portions Copyright (c) 1996,1997 by Internet Software Consortium.
|
||
|
*
|
||
|
* Permission to use, copy, modify, and distribute this software for any
|
||
|
* purpose with or without fee is hereby granted, provided that the above
|
||
|
* copyright notice and this permission notice appear in all copies.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
||
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
||
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||
|
* SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
/*#define DEBUG "tree"*/
|
||
|
|
||
|
#include "port_before.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "port_after.h"
|
||
|
|
||
|
#include <isc/memcluster.h>
|
||
|
#include <isc/tree.h>
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
static int debugDepth = 0;
|
||
|
static char *debugFuncs[256];
|
||
|
# define ENTER(proc) { \
|
||
|
debugFuncs[debugDepth] = proc; \
|
||
|
fprintf(stderr, "ENTER(%d:%s.%s)\n", \
|
||
|
debugDepth, DEBUG,
|
||
|
debugFuncs[debugDepth]); \
|
||
|
debugDepth++; \
|
||
|
}
|
||
|
# define RET(value) { \
|
||
|
debugDepth--; \
|
||
|
fprintf(stderr, "RET(%d:%s.%s)\n", \
|
||
|
debugDepth, DEBUG, \
|
||
|
debugFuncs[debugDepth]); \
|
||
|
return (value); \
|
||
|
}
|
||
|
# define RETV { \
|
||
|
debugDepth--; \
|
||
|
fprintf(stderr, "RETV(%d:%s.%s)\n", \
|
||
|
debugDepth, DEBUG, \
|
||
|
debugFuncs[debugDepth]); \
|
||
|
return; \
|
||
|
}
|
||
|
# define MSG(msg) fprintf(stderr, "MSG(%s)\n", msg);
|
||
|
#else
|
||
|
# define ENTER(proc) ;
|
||
|
# define RET(value) return (value);
|
||
|
# define RETV return;
|
||
|
# define MSG(msg) ;
|
||
|
#endif
|
||
|
|
||
|
#ifndef TRUE
|
||
|
# define TRUE 1
|
||
|
# define FALSE 0
|
||
|
#endif
|
||
|
|
||
|
static tree * sprout(tree **, tree_t, int *, int (*)(), void (*)());
|
||
|
static int delete(tree **, int (*)(), tree_t, void (*)(), int *, int *);
|
||
|
static void del(tree **, int *, tree **, void (*)(), int *);
|
||
|
static void bal_L(tree **, int *);
|
||
|
static void bal_R(tree **, int *);
|
||
|
|
||
|
void
|
||
|
tree_init(tree **ppr_tree) {
|
||
|
ENTER("tree_init")
|
||
|
*ppr_tree = NULL;
|
||
|
RETV
|
||
|
}
|
||
|
|
||
|
tree_t
|
||
|
tree_srch(tree **ppr_tree, int (*pfi_compare)(), tree_t p_user) {
|
||
|
ENTER("tree_srch")
|
||
|
|
||
|
if (*ppr_tree) {
|
||
|
int i_comp = (*pfi_compare)(p_user, (**ppr_tree).data);
|
||
|
|
||
|
if (i_comp > 0)
|
||
|
RET(tree_srch(&(**ppr_tree).right,
|
||
|
pfi_compare,
|
||
|
p_user))
|
||
|
|
||
|
if (i_comp < 0)
|
||
|
RET(tree_srch(&(**ppr_tree).left,
|
||
|
pfi_compare,
|
||
|
p_user))
|
||
|
|
||
|
/* not higher, not lower... this must be the one.
|
||
|
*/
|
||
|
RET((**ppr_tree).data)
|
||
|
}
|
||
|
|
||
|
/* grounded. NOT found.
|
||
|
*/
|
||
|
RET(NULL)
|
||
|
}
|
||
|
|
||
|
tree_t
|
||
|
tree_add(tree **ppr_tree, int (*pfi_compare)(),
|
||
|
tree_t p_user, void (*pfv_uar)())
|
||
|
{
|
||
|
int i_balance = FALSE;
|
||
|
|
||
|
ENTER("tree_add")
|
||
|
if (!sprout(ppr_tree, p_user, &i_balance, pfi_compare, pfv_uar))
|
||
|
RET(NULL)
|
||
|
RET(p_user)
|
||
|
}
|
||
|
|
||
|
int
|
||
|
tree_delete(tree **ppr_p, int (*pfi_compare)(),
|
||
|
tree_t p_user, void (*pfv_uar)())
|
||
|
{
|
||
|
int i_balance = FALSE, i_uar_called = FALSE;
|
||
|
|
||
|
ENTER("tree_delete");
|
||
|
RET(delete(ppr_p, pfi_compare, p_user, pfv_uar,
|
||
|
&i_balance, &i_uar_called))
|
||
|
}
|
||
|
|
||
|
int
|
||
|
tree_trav(tree **ppr_tree, int (*pfi_uar)()) {
|
||
|
ENTER("tree_trav")
|
||
|
|
||
|
if (!*ppr_tree)
|
||
|
RET(TRUE)
|
||
|
|
||
|
if (!tree_trav(&(**ppr_tree).left, pfi_uar))
|
||
|
RET(FALSE)
|
||
|
if (!(*pfi_uar)((**ppr_tree).data))
|
||
|
RET(FALSE)
|
||
|
if (!tree_trav(&(**ppr_tree).right, pfi_uar))
|
||
|
RET(FALSE)
|
||
|
RET(TRUE)
|
||
|
}
|
||
|
|
||
|
void
|
||
|
tree_mung(tree **ppr_tree, void (*pfv_uar)()) {
|
||
|
ENTER("tree_mung")
|
||
|
if (*ppr_tree) {
|
||
|
tree_mung(&(**ppr_tree).left, pfv_uar);
|
||
|
tree_mung(&(**ppr_tree).right, pfv_uar);
|
||
|
if (pfv_uar)
|
||
|
(*pfv_uar)((**ppr_tree).data);
|
||
|
memput(*ppr_tree, sizeof(tree));
|
||
|
*ppr_tree = NULL;
|
||
|
}
|
||
|
RETV
|
||
|
}
|
||
|
|
||
|
static tree *
|
||
|
sprout(tree **ppr, tree_t p_data, int *pi_balance,
|
||
|
int (*pfi_compare)(), void (*pfv_delete)())
|
||
|
{
|
||
|
tree *p1, *p2, *sub;
|
||
|
int cmp;
|
||
|
|
||
|
ENTER("sprout")
|
||
|
|
||
|
/* are we grounded? if so, add the node "here" and set the rebalance
|
||
|
* flag, then exit.
|
||
|
*/
|
||
|
if (!*ppr) {
|
||
|
MSG("grounded. adding new node, setting h=true")
|
||
|
*ppr = (tree *) memget(sizeof(tree));
|
||
|
if (*ppr) {
|
||
|
(*ppr)->left = NULL;
|
||
|
(*ppr)->right = NULL;
|
||
|
(*ppr)->bal = 0;
|
||
|
(*ppr)->data = p_data;
|
||
|
*pi_balance = TRUE;
|
||
|
}
|
||
|
RET(*ppr);
|
||
|
}
|
||
|
|
||
|
/* compare the data using routine passed by caller.
|
||
|
*/
|
||
|
cmp = (*pfi_compare)(p_data, (*ppr)->data);
|
||
|
|
||
|
/* if LESS, prepare to move to the left.
|
||
|
*/
|
||
|
if (cmp < 0) {
|
||
|
MSG("LESS. sprouting left.")
|
||
|
sub = sprout(&(*ppr)->left, p_data, pi_balance,
|
||
|
pfi_compare, pfv_delete);
|
||
|
if (sub && *pi_balance) { /* left branch has grown */
|
||
|
MSG("LESS: left branch has grown")
|
||
|
switch ((*ppr)->bal) {
|
||
|
case 1:
|
||
|
/* right branch WAS longer; bal is ok now */
|
||
|
MSG("LESS: case 1.. bal restored implicitly")
|
||
|
(*ppr)->bal = 0;
|
||
|
*pi_balance = FALSE;
|
||
|
break;
|
||
|
case 0:
|
||
|
/* balance WAS okay; now left branch longer */
|
||
|
MSG("LESS: case 0.. balnce bad but still ok")
|
||
|
(*ppr)->bal = -1;
|
||
|
break;
|
||
|
case -1:
|
||
|
/* left branch was already too long. rebal */
|
||
|
MSG("LESS: case -1: rebalancing")
|
||
|
p1 = (*ppr)->left;
|
||
|
if (p1->bal == -1) { /* LL */
|
||
|
MSG("LESS: single LL")
|
||
|
(*ppr)->left = p1->right;
|
||
|
p1->right = *ppr;
|
||
|
(*ppr)->bal = 0;
|
||
|
*ppr = p1;
|
||
|
} else { /* double LR */
|
||
|
MSG("LESS: double LR")
|
||
|
|
||
|
p2 = p1->right;
|
||
|
p1->right = p2->left;
|
||
|
p2->left = p1;
|
||
|
|
||
|
(*ppr)->left = p2->right;
|
||
|
p2->right = *ppr;
|
||
|
|
||
|
if (p2->bal == -1)
|
||
|
(*ppr)->bal = 1;
|
||
|
else
|
||
|
(*ppr)->bal = 0;
|
||
|
|
||
|
if (p2->bal == 1)
|
||
|
p1->bal = -1;
|
||
|
else
|
||
|
p1->bal = 0;
|
||
|
*ppr = p2;
|
||
|
} /*else*/
|
||
|
(*ppr)->bal = 0;
|
||
|
*pi_balance = FALSE;
|
||
|
} /*switch*/
|
||
|
} /*if*/
|
||
|
RET(sub)
|
||
|
} /*if*/
|
||
|
|
||
|
/* if MORE, prepare to move to the right.
|
||
|
*/
|
||
|
if (cmp > 0) {
|
||
|
MSG("MORE: sprouting to the right")
|
||
|
sub = sprout(&(*ppr)->right, p_data, pi_balance,
|
||
|
pfi_compare, pfv_delete);
|
||
|
if (sub && *pi_balance) {
|
||
|
MSG("MORE: right branch has grown")
|
||
|
|
||
|
switch ((*ppr)->bal) {
|
||
|
case -1:
|
||
|
MSG("MORE: balance was off, fixed implicitly")
|
||
|
(*ppr)->bal = 0;
|
||
|
*pi_balance = FALSE;
|
||
|
break;
|
||
|
case 0:
|
||
|
MSG("MORE: balance was okay, now off but ok")
|
||
|
(*ppr)->bal = 1;
|
||
|
break;
|
||
|
case 1:
|
||
|
MSG("MORE: balance was off, need to rebalance")
|
||
|
p1 = (*ppr)->right;
|
||
|
if (p1->bal == 1) { /* RR */
|
||
|
MSG("MORE: single RR")
|
||
|
(*ppr)->right = p1->left;
|
||
|
p1->left = *ppr;
|
||
|
(*ppr)->bal = 0;
|
||
|
*ppr = p1;
|
||
|
} else { /* double RL */
|
||
|
MSG("MORE: double RL")
|
||
|
|
||
|
p2 = p1->left;
|
||
|
p1->left = p2->right;
|
||
|
p2->right = p1;
|
||
|
|
||
|
(*ppr)->right = p2->left;
|
||
|
p2->left = *ppr;
|
||
|
|
||
|
if (p2->bal == 1)
|
||
|
(*ppr)->bal = -1;
|
||
|
else
|
||
|
(*ppr)->bal = 0;
|
||
|
|
||
|
if (p2->bal == -1)
|
||
|
p1->bal = 1;
|
||
|
else
|
||
|
p1->bal = 0;
|
||
|
|
||
|
*ppr = p2;
|
||
|
} /*else*/
|
||
|
(*ppr)->bal = 0;
|
||
|
*pi_balance = FALSE;
|
||
|
} /*switch*/
|
||
|
} /*if*/
|
||
|
RET(sub)
|
||
|
} /*if*/
|
||
|
|
||
|
/* not less, not more: this is the same key! replace...
|
||
|
*/
|
||
|
MSG("FOUND: Replacing data value")
|
||
|
*pi_balance = FALSE;
|
||
|
if (pfv_delete)
|
||
|
(*pfv_delete)((*ppr)->data);
|
||
|
(*ppr)->data = p_data;
|
||
|
RET(*ppr)
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
delete(tree **ppr_p, int (*pfi_compare)(), tree_t p_user,
|
||
|
void (*pfv_uar)(), int *pi_balance, int *pi_uar_called)
|
||
|
{
|
||
|
tree *pr_q;
|
||
|
int i_comp, i_ret;
|
||
|
|
||
|
ENTER("delete")
|
||
|
|
||
|
if (*ppr_p == NULL) {
|
||
|
MSG("key not in tree")
|
||
|
RET(FALSE)
|
||
|
}
|
||
|
|
||
|
i_comp = (*pfi_compare)((*ppr_p)->data, p_user);
|
||
|
if (i_comp > 0) {
|
||
|
MSG("too high - scan left")
|
||
|
i_ret = delete(&(*ppr_p)->left, pfi_compare, p_user, pfv_uar,
|
||
|
pi_balance, pi_uar_called);
|
||
|
if (*pi_balance)
|
||
|
bal_L(ppr_p, pi_balance);
|
||
|
} else if (i_comp < 0) {
|
||
|
MSG("too low - scan right")
|
||
|
i_ret = delete(&(*ppr_p)->right, pfi_compare, p_user, pfv_uar,
|
||
|
pi_balance, pi_uar_called);
|
||
|
if (*pi_balance)
|
||
|
bal_R(ppr_p, pi_balance);
|
||
|
} else {
|
||
|
MSG("equal")
|
||
|
pr_q = *ppr_p;
|
||
|
if (pr_q->right == NULL) {
|
||
|
MSG("right subtree null")
|
||
|
*ppr_p = pr_q->left;
|
||
|
*pi_balance = TRUE;
|
||
|
} else if (pr_q->left == NULL) {
|
||
|
MSG("right subtree non-null, left subtree null")
|
||
|
*ppr_p = pr_q->right;
|
||
|
*pi_balance = TRUE;
|
||
|
} else {
|
||
|
MSG("neither subtree null")
|
||
|
del(&pr_q->left, pi_balance, &pr_q,
|
||
|
pfv_uar, pi_uar_called);
|
||
|
if (*pi_balance)
|
||
|
bal_L(ppr_p, pi_balance);
|
||
|
}
|
||
|
if (!*pi_uar_called && pfv_uar)
|
||
|
(*pfv_uar)(pr_q->data);
|
||
|
/* Thanks to wuth@castrov.cuc.ab.ca for the following stmt. */
|
||
|
memput(pr_q, sizeof(tree));
|
||
|
i_ret = TRUE;
|
||
|
}
|
||
|
RET(i_ret)
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
del(tree **ppr_r, int *pi_balance, tree **ppr_q,
|
||
|
void (*pfv_uar)(), int *pi_uar_called)
|
||
|
{
|
||
|
ENTER("del")
|
||
|
|
||
|
if ((*ppr_r)->right != NULL) {
|
||
|
del(&(*ppr_r)->right, pi_balance, ppr_q,
|
||
|
pfv_uar, pi_uar_called);
|
||
|
if (*pi_balance)
|
||
|
bal_R(ppr_r, pi_balance);
|
||
|
} else {
|
||
|
if (pfv_uar)
|
||
|
(*pfv_uar)((*ppr_q)->data);
|
||
|
*pi_uar_called = TRUE;
|
||
|
(*ppr_q)->data = (*ppr_r)->data;
|
||
|
*ppr_q = *ppr_r;
|
||
|
*ppr_r = (*ppr_r)->left;
|
||
|
*pi_balance = TRUE;
|
||
|
}
|
||
|
|
||
|
RETV
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
bal_L(tree **ppr_p, int *pi_balance) {
|
||
|
tree *p1, *p2;
|
||
|
int b1, b2;
|
||
|
|
||
|
ENTER("bal_L")
|
||
|
MSG("left branch has shrunk")
|
||
|
|
||
|
switch ((*ppr_p)->bal) {
|
||
|
case -1:
|
||
|
MSG("was imbalanced, fixed implicitly")
|
||
|
(*ppr_p)->bal = 0;
|
||
|
break;
|
||
|
case 0:
|
||
|
MSG("was okay, is now one off")
|
||
|
(*ppr_p)->bal = 1;
|
||
|
*pi_balance = FALSE;
|
||
|
break;
|
||
|
case 1:
|
||
|
MSG("was already off, this is too much")
|
||
|
p1 = (*ppr_p)->right;
|
||
|
b1 = p1->bal;
|
||
|
if (b1 >= 0) {
|
||
|
MSG("single RR")
|
||
|
(*ppr_p)->right = p1->left;
|
||
|
p1->left = *ppr_p;
|
||
|
if (b1 == 0) {
|
||
|
MSG("b1 == 0")
|
||
|
(*ppr_p)->bal = 1;
|
||
|
p1->bal = -1;
|
||
|
*pi_balance = FALSE;
|
||
|
} else {
|
||
|
MSG("b1 != 0")
|
||
|
(*ppr_p)->bal = 0;
|
||
|
p1->bal = 0;
|
||
|
}
|
||
|
*ppr_p = p1;
|
||
|
} else {
|
||
|
MSG("double RL")
|
||
|
p2 = p1->left;
|
||
|
b2 = p2->bal;
|
||
|
p1->left = p2->right;
|
||
|
p2->right = p1;
|
||
|
(*ppr_p)->right = p2->left;
|
||
|
p2->left = *ppr_p;
|
||
|
if (b2 == 1)
|
||
|
(*ppr_p)->bal = -1;
|
||
|
else
|
||
|
(*ppr_p)->bal = 0;
|
||
|
if (b2 == -1)
|
||
|
p1->bal = 1;
|
||
|
else
|
||
|
p1->bal = 0;
|
||
|
*ppr_p = p2;
|
||
|
p2->bal = 0;
|
||
|
}
|
||
|
}
|
||
|
RETV
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
bal_R(tree **ppr_p, int *pi_balance) {
|
||
|
tree *p1, *p2;
|
||
|
int b1, b2;
|
||
|
|
||
|
ENTER("bal_R")
|
||
|
MSG("right branch has shrunk")
|
||
|
switch ((*ppr_p)->bal) {
|
||
|
case 1:
|
||
|
MSG("was imbalanced, fixed implicitly")
|
||
|
(*ppr_p)->bal = 0;
|
||
|
break;
|
||
|
case 0:
|
||
|
MSG("was okay, is now one off")
|
||
|
(*ppr_p)->bal = -1;
|
||
|
*pi_balance = FALSE;
|
||
|
break;
|
||
|
case -1:
|
||
|
MSG("was already off, this is too much")
|
||
|
p1 = (*ppr_p)->left;
|
||
|
b1 = p1->bal;
|
||
|
if (b1 <= 0) {
|
||
|
MSG("single LL")
|
||
|
(*ppr_p)->left = p1->right;
|
||
|
p1->right = *ppr_p;
|
||
|
if (b1 == 0) {
|
||
|
MSG("b1 == 0")
|
||
|
(*ppr_p)->bal = -1;
|
||
|
p1->bal = 1;
|
||
|
*pi_balance = FALSE;
|
||
|
} else {
|
||
|
MSG("b1 != 0")
|
||
|
(*ppr_p)->bal = 0;
|
||
|
p1->bal = 0;
|
||
|
}
|
||
|
*ppr_p = p1;
|
||
|
} else {
|
||
|
MSG("double LR")
|
||
|
p2 = p1->right;
|
||
|
b2 = p2->bal;
|
||
|
p1->right = p2->left;
|
||
|
p2->left = p1;
|
||
|
(*ppr_p)->left = p2->right;
|
||
|
p2->right = *ppr_p;
|
||
|
if (b2 == -1)
|
||
|
(*ppr_p)->bal = 1;
|
||
|
else
|
||
|
(*ppr_p)->bal = 0;
|
||
|
if (b2 == 1)
|
||
|
p1->bal = -1;
|
||
|
else
|
||
|
p1->bal = 0;
|
||
|
*ppr_p = p2;
|
||
|
p2->bal = 0;
|
||
|
}
|
||
|
}
|
||
|
RETV
|
||
|
}
|