Wed, 10 Mar 2004 17:08:26 +0100
trunk: changeset 1368
Added code for red-black trees.
LICENSE | file | annotate | diff | comparison | revisions | |
Makefile | file | annotate | diff | comparison | revisions | |
README | file | annotate | diff | comparison | revisions | |
README.rb | file | annotate | diff | comparison | revisions | |
rb-test.c | file | annotate | diff | comparison | revisions | |
rb.c | file | annotate | diff | comparison | revisions | |
rb.h | file | annotate | diff | comparison | revisions |
--- a/LICENSE Sun Mar 07 22:45:24 2004 +0100 +++ b/LICENSE Wed Mar 10 17:08:26 2004 +0100 @@ -1,7 +1,7 @@ -You may distribute and modify this library under the terms of either -the Clarified Artistic License or the GNU LGPL, version 2.1 or later, -both reproduced below. +The Red-Black tree code is under the GNU LGPL. The rest of the files +you may distribute and modify under either under the Clarified Artistic +License or the GNU LGPL, version 2.1 or later, both reproduced below. -------------------
--- a/Makefile Sun Mar 07 22:45:24 2004 +0100 +++ b/Makefile Wed Mar 10 17:08:26 2004 +0100 @@ -12,7 +12,7 @@ CFLAGS += $(C89_SOURCE) $(POSIX_SOURCE) SOURCES=misc.c output.c util.c optparser.c parser.c tokenizer.c \ - map.c obj.c objlist.c errorlog.c symlist.c + map.c obj.c objlist.c errorlog.c symlist.c rb.c ifdef LIBTU_NO_ERRMSG DEFINES += -DLIBTU_NO_ERRMSG
--- a/README Sun Mar 07 22:45:24 2004 +0100 +++ b/README Wed Mar 10 17:08:26 2004 +0100 @@ -5,10 +5,12 @@ <tuomov at iki.fi> -Libtu is a small utility library for programs written in C. You may -distribute and modify it under the terms of either the Clarified -Artistic License or the GNU LGPL, version 2.1 or later, both reproduced -in the file LICENSE. +Libtu is a small utility library for programs written in C. + +Most of this library may be distributed and modified under either under +the Clarified Artistic License or the GNU LGPL, version 2.1 or later, +both reproduced in the file LICENSE. The red-black tree code is under +the GNU LGPL; see README.rb for details. To build the library, first edit system.mk to customize it for your system if necessary. Then 'make depend && make'.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.rb Wed Mar 10 17:08:26 2004 +0100 @@ -0,0 +1,113 @@ +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--------------------------------------------------------------------------- +Jim Plank +plank@cs.utk.edu +http://www.cs.utk.edu/~plank + +Department of Computer Science +University of Tennessee +107 Ayres Hall +Knoxville, TN 37996 + +615-974-4397 +$Revision: 1.2 $ +--------------------------------------------------------------------------- + +Rb.c and rb.h are files for doing general red-black trees. + +The directory ansi contains ansi-standard c code for rb-trees (there +are some gross pointer casts there but don't worry about them. They +work). The directory non-ansi contains straight c code for rb-trees. +The ansi version can also be used by c++ (it has been tested). + +Rb.h contains the typedef for red-black tree structures. Basically, +red-black trees are balanced trees whose external nodes are sorted +by a key, and connected in a linked list. The following is how +you use rb.c and rb.h: + +Include rb.h in your source. + +Make_rb() returns the head of a red-black tree. It serves two functions: +Its p.root pointer points to the root of the red-black tree. Its +c.list.flink and c.list.blink pointers point to the first and last +external nodes of the tree. When the tree is empty, all these pointers +point to itself. + +The external nodes can be traversed in sorted order with their +c.list.flink and c.list.blink pointers. The macros rb_first, rb_last, +rb_next, rb_prev, and rb_traverse can be used to traverse external node lists. + +External nodes hold two pieces of information: the key and the value +(in k.key and v.val, respectively). The key can be a character +string, an integer, or a general pointer. Val is typed as a character +pointer, but can be any pointer. If the key is a character string, +then one can insert it, and a value into a tree with rb_insert(). If +it is an integer, then rb_inserti() should be used. If it is a general +pointer, then rb_insertg() must be used, with a comparison function +passed as the fourth argument. This function takes two keys as arguments, +and returns a negative value, positive value, or 0, depending on whether +the first key is less than, greater than or equal to the second. Thus, +one could use rb_insertg(t, s, v, strcmp) to insert the value v with +a character string s into the tree t. + +Rb_find_key(t, k) returns the external node in t whose value is equal +k or whose value is the smallest value greater than k. (Here, k is +a string). If there is no value greater than or equal to k, then +t is returned. Rb_find_ikey(t,k) works when k is an integer, and +Rb_find_gkey(t,k,cmp) works for general pointers. + +Rb_find_key_n(t, k, n) is the same as rb_find_key, except that it +returns whether or not k was found in n (n is an int *). Rb_find_ikey_n +and Rb_find_gkey_n are the analogous routines for integer and general +keys. + +Rb_insert_b(e, k, v) makes a new external node with key k and val v, and +inserts it before the external node e. If e is the head of a tree, +then the new node is inserted at the end of the external node list. +If this insertion violates sorted order, no error is flagged. It is +assumed that the user knows what he/she is doing. Rb_insert_a(e,k,v) +inserts the new node after e (if e = t, it inserts the new node at the +beginning of the list). + +Rb_insert() is therefore really a combination of Rb_find_key() and +Rb_insert_b(). + +Rb_delete_node(e) deletes the external node e from a tree (and thus from +the linked list of external nodes). The node is free'd as well, so +don't retain pointers to it. + +Red-black trees are spiffy because find_key, insert, and delete are all +done in log(n) time. Thus, they can be freely used instead of hash-tables, +with the benifit of having the elements in sorted order at all times, and +with the guarantee of operations being in log(n) time. + +Other routines: + +Rb_print_tree() will grossly print out a red-black tree with string keys. +Rb_iprint_tree() will do the same with trees with integer keys. +Rb_nblack(e) will report the number of black nodes on the path from external + node e to the root. The path length is less than twice this value. +Rb_plength(e) reports the length of the path from e to the root. + + +You can find a general description of red-black trees in any basic algorithms +text. E.g. ``Introduction to Algorithms'', by Cormen, Leiserson and Rivest +(McGraw Hill). An excellent and complete description of red-black trees +can also be found in Chapter 1 of Heather Booth's PhD disseratation: +``Some Fast Algorithms on Graphs and Trees'', Princeton University, 1990.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rb-test.c Wed Mar 10 17:08:26 2004 +0100 @@ -0,0 +1,98 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Revision 1.2. Jim Plank */ + +#include "rb.h" +#include <stdio.h> + +/* an example -- this allocates a red-black tree for integers. For a + * user-specified number of iterations, it does the following: + + * delete a random element in the tree. + * make two new random elements, and insert them into the tree + + * At the end, it prints the sorted list of elements, and then prints + * stats of the number of black nodes in any path in the tree, and + * the minimum and maximum path lengths. + + * Rb_find_ikey and rb_inserti could have been used, but instead + * rb_find_gkey and rb_insertg were used to show how they work. + + */ + +int icomp(char *i, char *j) +{ + int I, J; + + I = (int) i; + J = (int) j; + if (I == J) return 0; + if (I > J) return -1; else return 1; +} + +main(int argc, char **argv) +{ + int i, j, tb, nb, mxp, mnp, p; + int iterations; + Rb_node argt, t; + int *a; + + if (argc != 2) { + fprintf(stderr, "usage: main #iterations\n"); + exit(1); + } + argt = make_rb(); + srandom(time(0)); + iterations = atoi(argv[1]); + a = (int *) malloc (iterations*sizeof(int)); + + for (i = 0; i < atoi(argv[1]); i++) { + if (i > 0) { + j = random()%i; + + rb_delete_node(rb_find_gkey(argt, (char *) (a[j]), icomp)); + a[j] = random() % 1000; + rb_insertg(argt, (char *) (a[j]), NULL, icomp); + } + a[i] = random() % 1000; + rb_insertg(argt, (char *) (a[i]), NULL, icomp); + } + tb = 0; + mxp = 0; + mnp = 0; + rb_traverse(t, argt) { + printf("%d ", t->k.ikey); + nb = rb_nblack(t); + p = rb_plength(t); + if (tb == 0) { + tb = nb; + } else if (tb != nb) { + printf("Conflict: tb=%d, nb=%d\n", tb, nb); + exit(1); + } + mxp = (mxp == 0 || mxp < p) ? p : mxp; + mnp = (mnp == 0 || mxp > p) ? p : mnp; + } + printf("\n"); + + printf("Nblack = %d\n", tb); + printf("Max = %d\n", mxp); + printf("Min = %d\n", mnp); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rb.c Wed Mar 10 17:08:26 2004 +0100 @@ -0,0 +1,616 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include "rb.h" + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il); +static Rb_node lprev(Rb_node n); +static Rb_node rprev(Rb_node n); +static void recolor(Rb_node n); +static void single_rotate(Rb_node y, int l); +static void rb_print_tree(Rb_node t, int level); +static void rb_iprint_tree(Rb_node t, int level); + + +#define isred(n) (n->s.red) +#define isblack(n) (!isred(n)) +#define isleft(n) (n->s.left) +#define isright(n) (!isleft(n)) +#define isint(n) (n->s.internal) +#define isext(n) (!isint(n)) +#define ishead(n) (n->s.head) +#define isroot(n) (n->s.root) +#define setred(n) n->s.red = 1 +#define setblack(n) n->s.red = 0 +#define setleft(n) n->s.left = 1 +#define setright(n) n->s.left = 0 +#define sethead(n) n->s.head = 1 +#define setroot(n) n->s.root = 1 +#define setint(n) n->s.internal = 1 +#define setext(n) n->s.internal = 0 +#define setnormal(n) { n->s.root = 0; n ->s.head = 0; } +#define sibling(n) ((isleft(n)) ? n->p.parent->c.child.right \ + : n->p.parent->c.child.left) + +static void insert(Rb_node item, Rb_node list) /* Inserts to the end of a list */ +{ + Rb_node last_node; + + last_node = list->c.list.blink; + + list->c.list.blink = item; + last_node->c.list.flink = item; + item->c.list.blink = last_node; + item->c.list.flink = list; +} + +static void delete_item(Rb_node item) /* Deletes an arbitrary iterm */ +{ + item->c.list.flink->c.list.blink = item->c.list.blink; + item->c.list.blink->c.list.flink = item->c.list.flink; +} + +#define mk_new_ext(new, kkkey, vvval) {\ + new = (Rb_node) malloc(sizeof(struct rb_node));\ + new->v.val = vvval;\ + new->k.key = kkkey;\ + setext(new);\ + setblack(new);\ + setnormal(new);\ +} + +static void mk_new_int(Rb_node l, Rb_node r, Rb_node p, int il) +{ + Rb_node newnode; + + newnode = (Rb_node) malloc(sizeof(struct rb_node)); + setint(newnode); + setred(newnode); + setnormal(newnode); + newnode->c.child.left = l; + newnode->c.child.right = r; + newnode->p.parent = p; + newnode->k.lext = l; + newnode->v.rext = r; + l->p.parent = newnode; + r->p.parent = newnode; + setleft(l); + setright(r); + if (ishead(p)) { + p->p.root = newnode; + setroot(newnode); + } else if (il) { + setleft(newnode); + p->c.child.left = newnode; + } else { + setright(newnode); + p->c.child.right = newnode; + } + recolor(newnode); +} + + +Rb_node lprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isright(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node rprev(Rb_node n) +{ + if (ishead(n)) return n; + while (!isroot(n)) { + if (isleft(n)) return n->p.parent; + n = n->p.parent; + } + return n->p.parent; +} + +Rb_node make_rb() +{ + Rb_node head; + + head = (Rb_node) malloc (sizeof(struct rb_node)); + head->c.list.flink = head; + head->c.list.blink = head; + head->p.root = head; + head->k.key = ""; + sethead(head); + return head; +} + +Rb_node rb_find_key_n(Rb_node n, char *key, int *fnd) +{ + int cmp; + + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_key_n called on non-head 0x%x\n", n); + exit(1); + } + if (n->p.root == n) return n; + cmp = strcmp(key, n->c.list.blink->k.key); + if (cmp == 0) { + *fnd = 1; + return n->c.list.blink; + } + if (cmp > 0) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + cmp = strcmp(key, n->k.lext->k.key); + if (cmp == 0) { + *fnd = 1; + return n->k.lext; + } + if (cmp < 0) n = n->c.child.left ; else n = n->c.child.right; + } +} + +Rb_node rb_find_key(Rb_node n, char *key) +{ + int fnd; + return rb_find_key_n(n, key, &fnd); +} + +Rb_node rb_find_ikey_n(Rb_node n, int ikey, int *fnd) +{ + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_ikey_n called on non-head 0x%x\n", n); + exit(1); + } + if (n->p.root == n) return n; + if (ikey == n->c.list.blink->k.ikey) { + *fnd = 1; + return n->c.list.blink; + } + if (ikey > n->c.list.blink->k.ikey) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + if (ikey == n->k.lext->k.ikey) { + *fnd = 1; + return n->k.lext; + } + n = (ikey < n->k.lext->k.ikey) ? n->c.child.left : n->c.child.right; + } +} + +Rb_node rb_find_ikey(Rb_node n, int ikey) +{ + int fnd; + return rb_find_ikey_n(n, ikey, &fnd); +} + +Rb_node rb_find_gkey_n(Rb_node n, char *key,int (*fxn)(char *, char *), int *fnd) +{ + int cmp; + + *fnd = 0; + if (!ishead(n)) { + fprintf(stderr, "rb_find_key_n called on non-head 0x%x\n", n); + exit(1); + } + if (n->p.root == n) return n; + cmp = (*fxn)(key, n->c.list.blink->k.key); + if (cmp == 0) { + *fnd = 1; + return n->c.list.blink; + } + if (cmp > 0) return n; + else n = n->p.root; + while (1) { + if (isext(n)) return n; + cmp = (*fxn)(key, n->k.lext->k.key); + if (cmp == 0) { + *fnd = 1; + return n->k.lext; + } + if (cmp < 0) n = n->c.child.left ; else n = n->c.child.right; + } +} + +Rb_node rb_find_gkey(Rb_node n, char *key, int (*fxn)(char *, char *)) +{ + int fnd; + return rb_find_gkey_n(n, key, fxn, &fnd); +} + +Rb_node rb_insert_b(Rb_node n, char *key, char *val) +{ + Rb_node newleft, newright, newnode, list, p; + + if (ishead(n)) { + if (n->p.root == n) { /* Tree is empty */ + mk_new_ext(newnode, key, val); + insert(newnode, n); + n->p.root = newnode; + newnode->p.parent = n; + setroot(newnode); + return newnode; + } else { + mk_new_ext(newright, key, val); + insert(newright, n); + newleft = newright->c.list.blink; + setnormal(newleft); + mk_new_int(newleft, newright, newleft->p.parent, isleft(newleft)); + p = rprev(newright); + if (!ishead(p)) p->k.lext = newright; + return newright; + } + } else { + mk_new_ext(newleft, key, val); + insert(newleft, n); + setnormal(n); + mk_new_int(newleft, n, n->p.parent, isleft(n)); + p = lprev(newleft); + if (!ishead(p)) p->v.rext = newleft; + return newleft; + } +} + +static void recolor(Rb_node n) +{ + Rb_node p, gp, s; + int done = 0; + + while(!done) { + if (isroot(n)) { + setblack(n); + return; + } + + p = n->p.parent; + + if (isblack(p)) return; + + if (isroot(p)) { + setblack(p); + return; + } + + gp = p->p.parent; + s = sibling(p); + if (isred(s)) { + setblack(p); + setred(gp); + setblack(s); + n = gp; + } else { + done = 1; + } + } + /* p's sibling is black, p is red, gp is black */ + + if ((isleft(n) == 0) == (isleft(p) == 0)) { + single_rotate(gp, isleft(n)); + setblack(p); + setred(gp); + } else { + single_rotate(p, isleft(n)); + single_rotate(gp, isleft(n)); + setblack(n); + setred(gp); + } +} + +static void single_rotate(Rb_node y, int l) +{ + int rl, ir; + Rb_node x, yp; + char *tmp; + + ir = isroot(y); + yp = y->p.parent; + if (!ir) { + rl = isleft(y); + } + + if (l) { + x = y->c.child.left; + y->c.child.left = x->c.child.right; + setleft(y->c.child.left); + y->c.child.left->p.parent = y; + x->c.child.right = y; + setright(y); + } else { + x = y->c.child.right; + y->c.child.right = x->c.child.left; + setright(y->c.child.right); + y->c.child.right->p.parent = y; + x->c.child.left = y; + setleft(y); + } + + x->p.parent = yp; + y->p.parent = x; + if (ir) { + yp->p.root = x; + setnormal(y); + setroot(x); + } else { + if (rl) { + yp->c.child.left = x; + setleft(x); + } else { + yp->c.child.right = x; + setright(x); + } + } +} + +void rb_delete_node(Rb_node n) +{ + Rb_node s, p, gp; + char ir; + + if (isint(n)) { + fprintf(stderr, "Cannot delete an internal node: 0x%x\n", n); + exit(1); + } + if (ishead(n)) { + fprintf(stderr, "Cannot delete the head of an rb_tree: 0x%x\n", n); + exit(1); + } + delete_item(n); /* Delete it from the list */ + p = n->p.parent; /* The only node */ + if (isroot(n)) { + p->p.root = p; + free(n); + return; + } + s = sibling(n); /* The only node after deletion */ + if (isroot(p)) { + s->p.parent = p->p.parent; + s->p.parent->p.root = s; + setroot(s); + free(p); + free(n); + return; + } + gp = p->p.parent; /* Set parent to sibling */ + s->p.parent = gp; + if (isleft(p)) { + gp->c.child.left = s; + setleft(s); + } else { + gp->c.child.right = s; + setright(s); + } + ir = isred(p); + free(p); + free(n); + + if (isext(s)) { /* Update proper rext and lext values */ + p = lprev(s); + if (!ishead(p)) p->v.rext = s; + p = rprev(s); + if (!ishead(p)) p->k.lext = s; + } else if (isblack(s)) { + fprintf(stderr, "DELETION PROB -- sib is black, internal\n"); + exit(1); + } else { + p = lprev(s); + if (!ishead(p)) p->v.rext = s->c.child.left; + p = rprev(s); + if (!ishead(p)) p->k.lext = s->c.child.right; + setblack(s); + return; + } + + if (ir) return; + + /* Recolor */ + + n = s; + p = n->p.parent; + s = sibling(n); + while(isblack(p) && isblack(s) && isint(s) && + isblack(s->c.child.left) && isblack(s->c.child.right)) { + setred(s); + n = p; + if (isroot(n)) return; + p = n->p.parent; + s = sibling(n); + } + + if (isblack(p) && isred(s)) { /* Rotation 2.3b */ + single_rotate(p, isright(n)); + setred(p); + setblack(s); + s = sibling(n); + } + + { Rb_node x, z; char il; + + if (isext(s)) { + fprintf(stderr, "DELETION ERROR: sibling not internal\n"); + exit(1); + } + + il = isleft(n); + x = il ? s->c.child.left : s->c.child.right ; + z = sibling(x); + + if (isred(z)) { /* Rotation 2.3f */ + single_rotate(p, !il); + setblack(z); + if (isred(p)) setred(s); else setblack(s); + setblack(p); + } else if (isblack(x)) { /* Recoloring only (2.3c) */ + if (isred(s) || isblack(p)) { + fprintf(stderr, "DELETION ERROR: 2.3c not quite right\n"); + exit(1); + } + setblack(p); + setred(s); + return; + } else if (isred(p)) { /* 2.3d */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + setred(s); + return; + } else { /* 2.3e */ + single_rotate(s, il); + single_rotate(p, !il); + setblack(x); + return; + } + } +} + + +void rb_print_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree 0x%x is empty\n", t); + } else if (ishead(t)) { + printf("Head: 0x%x. Root = 0x%x\n", t, t->p.root); + rb_print_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node 0x%x: %c,%c: p=0x%x, k=%s\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->p.parent, t->k.key); + } else { + rb_print_tree(t->c.child.left, level+2); + rb_print_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node 0x%x: %c,%c: l=0x%x, r=0x%x, p=0x%x, lr=(%s,%s)\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->c.child.left, + t->c.child.right, + t->p.parent, t->k.lext->k.key, t->v.rext->k.key); + } + } +} + +void rb_iprint_tree(Rb_node t, int level) +{ + int i; + if (ishead(t) && t->p.parent == t) { + printf("tree 0x%x is empty\n", t); + } else if (ishead(t)) { + printf("Head: 0x%x. Root = 0x%x, < = 0x%x, > = 0x%x\n", + t, t->p.root, t->c.list.blink, t->c.list.flink); + rb_iprint_tree(t->p.root, 0); + } else { + if (isext(t)) { + for (i = 0; i < level; i++) putchar(' '); + printf("Ext node 0x%x: %c,%c: p=0x%x, <=0x%x, >=0x%x k=%d\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->p.parent, + t->c.list.blink, t->c.list.flink, t->k.ikey); + } else { + rb_iprint_tree(t->c.child.left, level+2); + rb_iprint_tree(t->c.child.right, level+2); + for (i = 0; i < level; i++) putchar(' '); + printf("Int node 0x%x: %c,%c: l=0x%x, r=0x%x, p=0x%x, lr=(%d,%d)\n", + t, isred(t)?'R':'B', isleft(t)?'l':'r', t->c.child.left, + t->c.child.right, + t->p.parent, t->k.lext->k.ikey, t->v.rext->k.ikey); + } + } +} + +int rb_nblack(Rb_node n) +{ + int nb; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_nblack called on a non-external node 0x%x\n", + n); + exit(1); + } + nb = 0; + while(!ishead(n)) { + if (isblack(n)) nb++; + n = n->p.parent; + } + return nb; +} + +int rb_plength(Rb_node n) +{ + int pl; + if (ishead(n) || isint(n)) { + fprintf(stderr, "ERROR: rb_plength called on a non-external node 0x%x\n", + n); + exit(1); + } + pl = 0; + while(!ishead(n)) { + pl++; + n = n->p.parent; + } + return pl; +} + +void rb_free_tree(Rb_node n) +{ + if (!ishead(n)) { + fprintf(stderr, "ERROR: Rb_free_tree called on a non-head node\n"); + exit(1); + } + + while(rb_first(n) != rb_nil(n)) { + rb_delete_node(rb_first(n)); + } + free(n); +} + +char *rb_val(Rb_node n) +{ + return n->v.val; +} + +Rb_node rb_insert_a(Rb_node nd, char *key, char *val) +{ + rb_insert_b(nd->c.list.flink, key, val); +} + +Rb_node rb_insert(Rb_node tree, char *key, char *val) +{ + rb_insert_b(rb_find_key(tree, key), key, val); +} + +Rb_node rb_inserti(Rb_node tree, int ikey, char *val) +{ + rb_insert_b(rb_find_ikey(tree, ikey), (char *) ikey, val); +} + +Rb_node rb_insertg(Rb_node tree, char *key, char *val, + int (*func)(char *, char *)) +{ + rb_insert_b(rb_find_gkey(tree, key, func), key, val); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rb.h Wed Mar 10 17:08:26 2004 +0100 @@ -0,0 +1,134 @@ +/* +Generic C code for red-black trees. +Copyright (C) 2000 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Revision 1.2. Jim Plank */ + +/* Original code by Jim Plank (plank@cs.utk.edu) */ +/* modified for THINK C 6.0 for Macintosh by Chris Bartley */ + +typedef struct { + unsigned red : 1 ; + unsigned internal : 1 ; + unsigned left : 1 ; + unsigned root : 1 ; + unsigned head : 1 ; +} status; + +/* Main rb_node. You only ever use the fields + + c.list.flink + c.list.blink + + k.key or k.ikey + v.val +*/ + + +typedef struct rb_node { + union { + struct { + struct rb_node *flink; + struct rb_node *blink; + } list; + struct { + struct rb_node *left; + struct rb_node *right; + } child; + } c; + union { + struct rb_node *parent; + struct rb_node *root; + } p; + status s; + union { + int ikey; + char *key; + struct rb_node *lext; + } k; + union { + char *val; + struct rb_node *rext; + } v; +} *Rb_node; + + +extern Rb_node make_rb(); /* Creates a new rb-tree */ + + + +/* Creates a node with key key and val val and inserts it into the tree. + rb_insert uses strcmp() as comparison funcion. rb_inserti uses <>=, + rb_insertg uses func() */ + +extern Rb_node rb_insert(Rb_node tree, char *key, char *val); +extern Rb_node rb_inserti(Rb_node tree, int ikey, char *val); +extern Rb_node rb_insertg(Rb_node tree, char *key, char *val, + int (*func)(char *, char *)); + + +/* returns an external node in t whose value is equal + k or whose value is the smallest value greater than k. */ + +extern Rb_node rb_find_key(Rb_node root, char *key); +extern Rb_node rb_find_ikey(Rb_node root, int ikey); +extern Rb_node rb_find_gkey(Rb_node root, char *key, + int (*func)(char *, char *)); + + +/* Works just like the find_key versions only it returns whether or not + it found the key in the integer variable found */ + +extern Rb_node rb_find_key_n(Rb_node root, char *key, int *found); +extern Rb_node rb_find_ikey_n(Rb_node root, int ikey, int *found); +extern Rb_node rb_find_gkey_n(Rb_node root, char *key, + int (*func)(char *, char *), int *found); + + +/* Creates a node with key key and val val and inserts it into the + tree before/after node nd. Does not check to ensure that you are + keeping the correct order */ + +extern Rb_node rb_insert_b(Rb_node nd, char *key, char *val); +extern Rb_node rb_insert_a(Rb_node nd, char *key, char *val); + + +extern void rb_delete_node(Rb_node node); /* Deletes and frees a node (but + not the key or val) */ +extern void rb_free_tree(Rb_node root); /* Deletes and frees an entire tree */ + +extern char *rb_val(Rb_node node); /* Returns node->v.val -- this is to shut + lint up */ + +extern int rb_nblack(Rb_node n); /* returns # of black nodes in path from + n to the root */ +int rb_plength(Rb_node n); /* returns the # of nodes in path from + n to the root */ + +#define rb_first(n) (n->c.list.flink) +#define rb_last(n) (n->c.list.blink) +#define rb_next(n) (n->c.list.flink) +#define rb_prev(n) (n->c.list.blink) +#define rb_empty(t) (t->c.list.flink == t) +#ifndef rb_nil +#define rb_nil(t) (t) +#endif + +#define rb_traverse(ptr, lst) \ + for(ptr = rb_first(lst); ptr != rb_nil(lst); ptr = rb_next(ptr)) +