Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
# makefile for tree stuff
# vix 24jul87 [stripped down from as makefile]
CFLAGS = -O
TRTEST_OBJ = t_trtest.o tree.o
all : t_trtest
t_trtest : $(TRTEST_OBJ)
cc -o t_trtest $(TRTEST_OBJ)
clean : FRC
rm -f core a.out t_trtest $(TRTEST_OBJ)
FRC :
tree.o : tree.c tree.h vixie.h
t_trtest.o : t_trtest.c tree.h vixie.h

View File

@@ -0,0 +1,23 @@
AVL Trees V1.0
24-July-1987
Paul Vixie
This library and test program are useful for creating and using balanced
binary trees (AVL trees). The tree is held in memory, using malloc(3) to
allocate storage. A better version would allow file-based trees in
addition; once memory mapped files hit the UNIX(tm) community, this will
be much easier to do. In the meanwhile, these routines have been very
useful to be for symbol tables and the like. (Yes, I'm sure hashing is
better in some way, but I've used this for symbol tables, just the same.)
I cannot take credit for the algorithms. See "Algorithms & Data Structures,"
Niklaus Wirth, Prentice-Hall 1986, ISBN 0-13-022005-1. This is an update of
Wirth's previous book, titled "Algorythms + Data Structures = Programs,"
which used Pascal as the language for examples. This later book uses the
newer Modula-2 for it's examples; this tree code was created using the
Modula-2 examples as guidelines. At the time I typed this stuff in (about
a year ago, in July 1987), I understood how it all worked. Today, well...
This code is hereby placed in the public domain, unless restrictions apply
from Prentice-Hall on the algorithms themselves. If you use or redistribute
this code, please leave my name (and Wirth's) in the comments.

View File

@@ -0,0 +1,126 @@
/* t_trtest - test the tree functions
* vix 24jul87 [documented, added savestr for net distribution]
*/
#define MAIN
#include <stdio.h>
#include "vixie.h"
#include "tree.h"
main()
{
tree *t;
char line[100];
tree_init(&t);
while (printf("line (or .): "), gets(line), line[0] != '.')
{
if (strncmp(line, "~r ", 3)) {
trtest(&t, line, 1);
}
else {
FILE *f;
if (!(f = fopen(&line[3], "r")))
perror(&line[3]);
else {
while (fgets(line, 100, f)) {
line[strlen(line)-1] = '\0';
printf("(%s)\n", line);
trtest(&t, line, 0);
}
fclose(f);
}
}
}
}
trtest(tt, line, inter)
tree **tt;
char *line;
{
char opts[100], *tree_srch(), *pc, *n;
int uar_print(), duar(), compar(), opt, status;
pc = tree_srch(tt, compar, line);
printf("tree_srch=%08lx\n", pc);
if (pc)
{
printf(" <%s>\n", pc);
if (inter) {
printf("delete? "); gets(opts); opt = (opts[0]=='y');
}
else
opt = 1;
if (opt) {
status = tree_delete(tt, compar, line, duar);
printf("delete=%d\n", status);
}
}
else
{
if (inter) {
printf("add? "); gets(opts); opt = (opts[0]=='y');
}
else
opt = 1;
if (opt) {
char *savestr();
n = savestr(line);
tree_add(tt, compar, n, duar);
}
}
tree_trav1(*tt, 0);
}
duar(pc)
char *pc;
{
printf("duar called, pc=%08X: <%s>\n", pc, pc?pc:"");
free(pc);
}
tree_trav1(t, l)
tree *t;
{
int i;
if (!t) return;
tree_trav1(t->tree_l, l+1);
for (i=0; i<l; i++) printf(" ");
printf("%08lx (%s)\n", t->tree_p, t->tree_p);
tree_trav1(t->tree_r, l+1);
}
uar_print(pc)
char *pc;
{
printf("uar_print(%08lx)", pc);
if (pc)
printf(" '%s'", pc);
putchar('\n');
return 1;
}
compar(l, r)
char *l, *r;
{
printf("compar(%s,%s)=%d\n", l, r, strcmp(l, r));
return strcmp(l, r);
}
char *
savestr(str)
char *str;
{
char *save;
save = malloc(strlen(str) + 1);
strcpy(save, str);
return save;
}

View File

@@ -0,0 +1,511 @@
/* as_tree - tree library for as
* vix 14dec85 [written]
* vix 02feb86 [added tree balancing from wirth "a+ds=p" p. 220-221]
* vix 06feb86 [added tree_mung()]
* vix 20jun86 [added tree_delete per wirth a+ds (mod2 v.) p. 224]
* vix 23jun86 [added delete uar to add for replaced nodes]
*/
/* 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. This code and associated documentation is hereby placed
* in the public domain.
*/
/*#define DEBUG "tree"*/
#include <stdio.h>
#include "vixie.h"
#include "tree.h"
#ifdef DEBUG
#define MSG(msg) printf("DEBUG: '%s'\n", msg);
#else
#define MSG(msg)
#endif
void tree_init(ppr_tree)
tree **ppr_tree;
{
ENTER("tree_init")
*ppr_tree = NULL;
EXITV
}
char *tree_srch(ppr_tree, pfi_compare, pc_user)
tree **ppr_tree;
int (*pfi_compare)();
char *pc_user;
{
register int i_comp;
register tree *pr_new;
ENTER("tree_srch")
if (*ppr_tree)
{
i_comp = (*pfi_compare)(pc_user, (**ppr_tree).tree_p);
if (i_comp > 0)
EXIT(tree_srch(
&(**ppr_tree).tree_r,
pfi_compare,
pc_user
))
if (i_comp < 0)
EXIT(tree_srch(
&(**ppr_tree).tree_l,
pfi_compare,
pc_user
))
/* not higher, not lower... this must be the one.
*/
EXIT((**ppr_tree).tree_p)
}
/* grounded. NOT found.
*/
EXIT(NULL)
}
void tree_add(ppr_tree, pfi_compare, pc_user, pfi_delete)
tree **ppr_tree;
int (*pfi_compare)();
char *pc_user;
int (*pfi_delete)();
{
void sprout();
int i_balance = FALSE;
ENTER("tree_add")
sprout(ppr_tree, pc_user, &i_balance, pfi_compare, pfi_delete);
EXITV
}
static void sprout(ppr, pc_data, pi_balance, pfi_compare, pfi_delete)
tree **ppr;
char *pc_data;
int *pi_balance;
int (*pfi_compare)();
int (*pfi_delete)();
{
tree *p1, *p2;
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 *) malloc(sizeof(tree));
(*ppr)->tree_l = NULL;
(*ppr)->tree_r = NULL;
(*ppr)->tree_b = 0;
(*ppr)->tree_p = pc_data;
*pi_balance = TRUE;
EXITV
}
/* compare the data using routine passed by caller.
*/
cmp = (*pfi_compare)(pc_data, (*ppr)->tree_p);
/* if LESS, prepare to move to the left.
*/
if (cmp < 0) {
MSG("LESS. sprouting left.")
sprout(&(*ppr)->tree_l, pc_data, pi_balance,
pfi_compare, pfi_delete);
if (*pi_balance) { /* left branch has grown longer */
MSG("LESS: left branch has grown")
switch ((*ppr)->tree_b)
{
case 1: /* right branch WAS longer; balance is ok now */
MSG("LESS: case 1.. balnce restored implicitly")
(*ppr)->tree_b = 0;
*pi_balance = FALSE;
break;
case 0: /* balance WAS okay; now left branch longer */
MSG("LESS: case 0.. balnce bad but still ok")
(*ppr)->tree_b = -1;
break;
case -1:
/* left branch was already too long. rebalnce */
MSG("LESS: case -1: rebalancing")
p1 = (*ppr)->tree_l;
if (p1->tree_b == -1) { /* LL */
MSG("LESS: single LL")
(*ppr)->tree_l = p1->tree_r;
p1->tree_r = *ppr;
(*ppr)->tree_b = 0;
*ppr = p1;
}
else { /* double LR */
MSG("LESS: double LR")
p2 = p1->tree_r;
p1->tree_r = p2->tree_l;
p2->tree_l = p1;
(*ppr)->tree_l = p2->tree_r;
p2->tree_r = *ppr;
if (p2->tree_b == -1)
(*ppr)->tree_b = 1;
else
(*ppr)->tree_b = 0;
if (p2->tree_b == 1)
p1->tree_b = -1;
else
p1->tree_b = 0;
*ppr = p2;
} /*else*/
(*ppr)->tree_b = 0;
*pi_balance = FALSE;
} /*switch*/
} /*if*/
EXITV
} /*if*/
/* if MORE, prepare to move to the right.
*/
if (cmp > 0) {
MSG("MORE: sprouting to the right")
sprout(&(*ppr)->tree_r, pc_data, pi_balance,
pfi_compare, pfi_delete);
if (*pi_balance) { /* right branch has grown longer */
MSG("MORE: right branch has grown")
switch ((*ppr)->tree_b)
{
case -1:MSG("MORE: balance was off, fixed implicitly")
(*ppr)->tree_b = 0;
*pi_balance = FALSE;
break;
case 0: MSG("MORE: balance was okay, now off but ok")
(*ppr)->tree_b = 1;
break;
case 1: MSG("MORE: balance was off, need to rebalance")
p1 = (*ppr)->tree_r;
if (p1->tree_b == 1) { /* RR */
MSG("MORE: single RR")
(*ppr)->tree_r = p1->tree_l;
p1->tree_l = *ppr;
(*ppr)->tree_b = 0;
*ppr = p1;
}
else { /* double RL */
MSG("MORE: double RL")
p2 = p1->tree_l;
p1->tree_l = p2->tree_r;
p2->tree_r = p1;
(*ppr)->tree_r = p2->tree_l;
p2->tree_l = *ppr;
if (p2->tree_b == 1)
(*ppr)->tree_b = -1;
else
(*ppr)->tree_b = 0;
if (p2->tree_b == -1)
p1->tree_b = 1;
else
p1->tree_b = 0;
*ppr = p2;
} /*else*/
(*ppr)->tree_b = 0;
*pi_balance = FALSE;
} /*switch*/
} /*if*/
EXITV
} /*if*/
/* not less, not more: this is the same key! replace...
*/
MSG("I found it! Replacing data value")
*pi_balance = FALSE;
if (pfi_delete)
(*pfi_delete)((*ppr)->tree_p);
(*ppr)->tree_p = pc_data;
EXITV
}
int tree_delete(ppr_p, pfi_compare, pc_user, pfi_uar)
tree **ppr_p;
int (*pfi_compare)();
char *pc_user;
int (*pfi_uar)();
{
int i_balance = FALSE, i_uar_called = FALSE;
ENTER("tree_delete");
EXIT(delete(ppr_p, pfi_compare, pc_user, pfi_uar,
&i_balance, &i_uar_called))
}
static int delete(ppr_p, pfi_compare, pc_user, pfi_uar,
pi_balance, pi_uar_called)
tree **ppr_p;
int (*pfi_compare)();
char *pc_user;
int (*pfi_uar)();
int *pi_balance;
int *pi_uar_called;
{
void del(), balanceL(), balanceR();
tree *pr_q;
int i_comp, i_ret;
ENTER("delete")
if (*ppr_p == NULL) {
MSG("key not in tree")
EXIT(FALSE)
}
i_comp = (*pfi_compare)((*ppr_p)->tree_p, pc_user);
if (i_comp > 0) {
MSG("too high - scan left")
i_ret = delete(&(*ppr_p)->tree_l, pfi_compare, pc_user, pfi_uar,
pi_balance, pi_uar_called);
if (*pi_balance)
balanceL(ppr_p, pi_balance);
}
else if (i_comp < 0) {
MSG("too low - scan right")
i_ret = delete(&(*ppr_p)->tree_r, pfi_compare, pc_user, pfi_uar,
pi_balance, pi_uar_called);
if (*pi_balance)
balanceR(ppr_p, pi_balance);
}
else {
MSG("equal")
pr_q = *ppr_p;
if (pr_q->tree_r == NULL) {
MSG("right subtree null")
*ppr_p = pr_q->tree_l;
*pi_balance = TRUE;
}
else if (pr_q->tree_l == NULL) {
MSG("right subtree non-null, left subtree null")
*ppr_p = pr_q->tree_r;
*pi_balance = TRUE;
}
else {
MSG("neither subtree null")
del(&pr_q->tree_l, pi_balance, &pr_q, pfi_uar,
pi_uar_called);
if (*pi_balance)
balanceL(ppr_p, pi_balance);
}
free(pr_q);
if (!*pi_uar_called && *pfi_uar)
(*pfi_uar)(pr_q->tree_p);
i_ret = TRUE;
}
EXIT(i_ret)
}
static void del(ppr_r, pi_balance, ppr_q, pfi_uar, pi_uar_called)
tree **ppr_r;
int *pi_balance;
tree **ppr_q;
int (*pfi_uar)();
int *pi_uar_called;
{
void balanceR();
ENTER("del")
if ((*ppr_r)->tree_r != NULL) {
del(&(*ppr_r)->tree_r, pi_balance, ppr_q, pfi_uar,
pi_uar_called);
if (*pi_balance)
balanceR(ppr_r, pi_balance);
} else {
if (pfi_uar)
(*pfi_uar)((*ppr_q)->tree_p);
*pi_uar_called = TRUE;
(*ppr_q)->tree_p = (*ppr_r)->tree_p;
*ppr_q = *ppr_r;
*ppr_r = (*ppr_r)->tree_l;
*pi_balance = TRUE;
}
EXITV
}
static void balanceL(ppr_p, pi_balance)
tree **ppr_p;
int *pi_balance;
{
tree *p1, *p2;
int b1, b2;
ENTER("balanceL")
MSG("left branch has shrunk")
switch ((*ppr_p)->tree_b)
{
case -1: MSG("was imbalanced, fixed implicitly")
(*ppr_p)->tree_b = 0;
break;
case 0: MSG("was okay, is now one off")
(*ppr_p)->tree_b = 1;
*pi_balance = FALSE;
break;
case 1: MSG("was already off, this is too much")
p1 = (*ppr_p)->tree_r;
b1 = p1->tree_b;
if (b1 >= 0) {
MSG("single RR")
(*ppr_p)->tree_r = p1->tree_l;
p1->tree_l = *ppr_p;
if (b1 == 0) {
MSG("b1 == 0")
(*ppr_p)->tree_b = 1;
p1->tree_b = -1;
*pi_balance = FALSE;
} else {
MSG("b1 != 0")
(*ppr_p)->tree_b = 0;
p1->tree_b = 0;
}
*ppr_p = p1;
} else {
MSG("double RL")
p2 = p1->tree_l;
b2 = p2->tree_b;
p1->tree_l = p2->tree_r;
p2->tree_r = p1;
(*ppr_p)->tree_r = p2->tree_l;
p2->tree_l = *ppr_p;
if (b2 == 1)
(*ppr_p)->tree_b = -1;
else
(*ppr_p)->tree_b = 0;
if (b2 == -1)
p1->tree_b = 1;
else
p1->tree_b = 0;
*ppr_p = p2;
p2->tree_b = 0;
}
}
EXITV
}
static void balanceR(ppr_p, pi_balance)
tree **ppr_p;
int *pi_balance;
{
tree *p1, *p2;
int b1, b2;
ENTER("balanceR")
MSG("right branch has shrunk")
switch ((*ppr_p)->tree_b)
{
case 1: MSG("was imbalanced, fixed implicitly")
(*ppr_p)->tree_b = 0;
break;
case 0: MSG("was okay, is now one off")
(*ppr_p)->tree_b = -1;
*pi_balance = FALSE;
break;
case -1: MSG("was already off, this is too much")
p1 = (*ppr_p)->tree_l;
b1 = p1->tree_b;
if (b1 <= 0) {
MSG("single LL")
(*ppr_p)->tree_l = p1->tree_r;
p1->tree_r = *ppr_p;
if (b1 == 0) {
MSG("b1 == 0")
(*ppr_p)->tree_b = -1;
p1->tree_b = 1;
*pi_balance = FALSE;
} else {
MSG("b1 != 0")
(*ppr_p)->tree_b = 0;
p1->tree_b = 0;
}
*ppr_p = p1;
} else {
MSG("double LR")
p2 = p1->tree_r;
b2 = p2->tree_b;
p1->tree_r = p2->tree_l;
p2->tree_l = p1;
(*ppr_p)->tree_l = p2->tree_r;
p2->tree_r = *ppr_p;
if (b2 == -1)
(*ppr_p)->tree_b = 1;
else
(*ppr_p)->tree_b = 0;
if (b2 == 1)
p1->tree_b = -1;
else
p1->tree_b = 0;
*ppr_p = p2;
p2->tree_b = 0;
}
}
EXITV
}
int tree_trav(ppr_tree, pfi_uar)
tree **ppr_tree;
int (*pfi_uar)();
{
ENTER("tree_trav")
if (!*ppr_tree)
EXIT(TRUE)
if (!tree_trav(&(**ppr_tree).tree_l, pfi_uar))
EXIT(FALSE)
if (!(*pfi_uar)((**ppr_tree).tree_p))
EXIT(FALSE)
if (!tree_trav(&(**ppr_tree).tree_r, pfi_uar))
EXIT(FALSE)
EXIT(TRUE)
}
void tree_mung(ppr_tree, pfi_uar)
tree **ppr_tree;
int (*pfi_uar)();
{
ENTER("tree_mung")
if (*ppr_tree)
{
tree_mung(&(**ppr_tree).tree_l, pfi_uar);
tree_mung(&(**ppr_tree).tree_r, pfi_uar);
if (pfi_uar)
(*pfi_uar)((**ppr_tree).tree_p);
free(*ppr_tree);
*ppr_tree = NULL;
}
EXITV
}

View File

@@ -0,0 +1,19 @@
/* tree.h - declare structures used by tree.c
* vix 27jun86 [broken out of tree.c]
*/
#ifndef _TREE_FLAG
#define _TREE_FLAG
typedef struct tree_s
{
struct tree_s *tree_l, *tree_r;
short tree_b;
char *tree_p;
}
tree;
#endif _TREE_FLAG

View File

@@ -0,0 +1,124 @@
.TH TREE 2 "23 June 1986"
.UC 4
.SH NAME
tree_init, tree_mung, tree_srch, tree_add, tree_delete, tree_trav \- balanced binary tree routines
.SH SYNOPSIS
.nf
.B void
.B tree_init(tree)
.B int **tree;
.PP
.B int *
.B tree_srch(tree, compare, data)
.B int **tree, (*compare)(), *data;
.PP
.B void
.B tree_add(tree, compare, data, del_uar)
.B int **tree, (*compare)(), *data, (*del_uar)();
.PP
.B int
.B tree_delete(tree, compare, data, del_uar)
.B int **tree, (*compare)(), *data, (*del_uar)();
.PP
.B int
.B tree_trav(tree, trav_uar)
.B int **tree, (*trav_uar)();
.PP
.B void
.B tree_mung(tree, del_uar)
.B int **tree, (*del_uar)();
.fi
.SH DESCRIPTION
These functions create and manipulate a balanced binary (AVL) tree. Each node
of the tree contains the expected left & right subtree pointers, a short-int
balance indicator, and a pointer to the user-data. On a 32-bit system, this
means an overhead of 4+4+2+4 bytes per node. There is no key data type
enforced by this package; a caller-supplied compare routine is used to compare
user-data blocks.
.PP
.I Tree_init
creates an empty tree and binds it to
.I tree
(which for this and all other routines in this package should be declared as
a pointer to integer and passed by reference), which can then be used by other
routines in this package. Note that more than one
.I tree
variable can exist at once; thus multiple trees can be manipulated
simultaneously.
.PP
.I Tree_srch
searches a tree for a specific node and returns either
.I NULL
if no node was found, or the address of the user-data for that node if one was
found.
.I compare
is the address of a function to compare two user-data blocks. This routine
should work much the way
.IR strcmp 2
does; in fact,
.I strcmp
could be used if the user-data was a null-terminated string.
.I data
is the address of a user-data block to be used via
.I compare
as the search criteria. The tree is searched for a node where
.I compare
returns 0.
.PP
.I Tree_add
inserts or replaces a node in the specified tree. The tree specified by
.I tree
is searched as in
.I tree_srch,
and if a node is found to match
.I data,
then the
.I del_uar
function is called with the address of the user-data block for the node
(this routine should deallocate any dynamic memory which is pointed to
exclusively by the node); the user-data pointer for the node is then
replaced by the value of
.I data.
If no node is found to match, a new node is added (which may or may not
cause a transparent rebalance operation), with a user-data pointer equal
to
.I data.
.PP
.I Tree_delete
deletes a node from
.I tree.
A rebalance may or may not occur, depending on where the node is removed from
and what the rest of the tree looks like.
.I Tree_delete
returns TRUE if a node was deleted, FALSE otherwise.
.PP
.I Tree_trav
traverses all of
.I tree,
calling
.I trav_uar
with the address of each user-data block. If
.I trav_uar
returns FALSE at any time,
.I tree_trav
will immediately return FALSE to its caller. Otherwise all nodes will be
reached and
.I tree_trav
will return TRUE.
.PP
.I Tree_mung
deletes every node in
.I tree,
calling
.I del_uar
with the user-data address from each node (see
.I tree_add
and
.I tree_delete
above). The tree is left in the same state that
.I tree_init
leaves it in \- i.e., empty.
.SH AUTHOR
Paul Vixie, converted and augumented from Modula-2 examples in
.I Algorithms & Data Structures,
Niklaus Wirth, Prentice-Hall, ISBN 0-13-022005-1.

View File

@@ -0,0 +1,73 @@
/* vixie.h - include file to define general vixie-type things
* v1.0 vix 21jun86 [broken out of as.h]
*/
#ifdef DOCUMENTATION
There are two macros you can define before including this file which can
change the things defined by this file.
DEBUG: if defined, will cause enter/exit messages to be printed by the
ENTER/EXIT/EXITV macros. If not defined, causes ENTER to do nothing,
and EXIT/EXITV to generate 'return' without any messages.
If defined, should be set to the name of the including module.
MAIN: Should be defined for a program containing a main() function which
is linked with other modules which include this file.
Value is not important, only existence/nonexistence matters.
#endif DOCUMENTATION
#ifndef _VIXIE_FLAG
#define _VIXIE_FLAG
/*--- debugging stuff ---*/
#define MAXPROC 256
#ifdef DEBUG
#define ENTER(proc) { \
APC_PROCS[I_PROC] = proc; \
printf("ENTER(%d:%s.%s)\n", \
I_PROC, DEBUG, APC_PROCS[I_PROC]); \
I_PROC++; \
}
#define EXIT(value) { \
I_PROC--; \
printf("EXIT(%d:%s.%s)\n", \
I_PROC, DEBUG, \
APC_PROCS[I_PROC]); \
return value; \
}
#define EXITV { \
I_PROC--; \
printf("EXITV(%d:%s.%s)\n", \
I_PROC, DEBUG, \
APC_PROCS[I_PROC]); \
return value; \
}
#else
#define ENTER(proc)
#define EXIT(value) {return value;}
#define EXITV return;
#endif
#ifdef MAIN
int I_PROC = 0;
char *APC_PROCS[MAXPROC];
#else
extern int I_PROC;
extern char *APC_PROCS[MAXPROC];
#endif
/*--- why didn't k&r put these into stdio.h? ---*/
#define TRUE 1
#define FALSE 0
extern char *malloc(), *calloc();
#endif _VIXIE_FLAG