From 910b2a809254c9482d15877bcb998ceaf6847986 Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 20 Jan 1999 10:18:34 +0000 Subject: [PATCH] working single-level red/black tree code --- lib/isc/include/isc/rbtgen.h | 16 +- lib/isc/rbtgen.c | 407 +++++++++++++++++++++++++++++------ 2 files changed, 359 insertions(+), 64 deletions(-) diff --git a/lib/isc/include/isc/rbtgen.h b/lib/isc/include/isc/rbtgen.h index 89d1f9aa95..7c8a9ee5e6 100644 --- a/lib/isc/include/isc/rbtgen.h +++ b/lib/isc/include/isc/rbtgen.h @@ -22,8 +22,16 @@ #include -typedef struct RBT_NODE *RBT_NODE_T; +typedef struct _isc_rbt_node { + enum { red, black } color; + void *data; + struct _isc_rbt_node *parent; + struct _isc_rbt_node *right; + struct _isc_rbt_node *left; +} RBT_NODE; -RBT_LINKAGE isc_result_t RBT_INSERT(RBT_NODE_T, RBT_NODE_T *); -RBT_LINKAGE RBT_NODE_T RBT_SEARCH(RBT_NODE_T, RBT_KEY_T); -RBT_LINKAGE void RBT_PRINT(RBT_NODE_T); +isc_result_t RBT_INSERT(RBT_NODE *, RBT_NODE **, + int (*compare)(void *, void*)); +isc_result_t RBT_DELETE(RBT_NODE *, RBT_NODE **); +RBT_NODE *RBT_SEARCH(RBT_NODE *, void *, int (*compare)(void *, void*)); +void RBT_PRINT(RBT_NODE *, void (*print_key)(void *)); diff --git a/lib/isc/rbtgen.c b/lib/isc/rbtgen.c index 902476c9c4..a900790fd4 100644 --- a/lib/isc/rbtgen.c +++ b/lib/isc/rbtgen.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -49,9 +50,26 @@ #define TRACE_NODE(n) #endif +#define COLOR(node) ((node)->color) +#define KEY(node) ((node)->data) +#define LEFT(node) ((node)->left) +#define RIGHT(node) ((node)->right) +#define PARENT(node) ((node)->parent) + +#define SET_COLOR(node, value) ((node)->color = (value)) +#define SET_KEY(node, value) ((node)->data = (value)) +#define SET_LEFT(node, child) ((node)->left = (child)) +#define SET_RIGHT(node, child) ((node)->right = (child)) +#define SET_PARENT(node, child) ((node)->parent = (child)) + +#define IS_RED(node) ((node) != NULL && (node)->color == red) +#define IS_BLACK(node) ((node) == NULL || (node)->color == black) +#define MAKE_RED(node) ((node)->color = red) +#define MAKE_BLACK(node) ((node)->color = black) + static inline void -rotate_left(RBT_NODE_T node, RBT_NODE_T parent, RBT_NODE_T *rootp) { - RBT_NODE_T child; +rotate_left(RBT_NODE *node, RBT_NODE **rootp) { + RBT_NODE *child; REQUIRE(node != NULL); REQUIRE(rootp != NULL); @@ -60,21 +78,26 @@ rotate_left(RBT_NODE_T node, RBT_NODE_T parent, RBT_NODE_T *rootp) { REQUIRE(child != NULL); SET_RIGHT(node, LEFT(child)); + if (LEFT(child) != NULL) + SET_PARENT(LEFT(child), node); + SET_LEFT(child, node); - if (parent != NULL) { - if (LEFT(parent) == node) - SET_LEFT(parent, child); + SET_PARENT(child, PARENT(node)); + if (PARENT(node) != NULL) { + if (LEFT(PARENT(node)) == node) + SET_LEFT(PARENT(node), child); else { - INSIST(RIGHT(parent) == node); /* XXX remove */ - SET_RIGHT(parent, child); + SET_RIGHT(PARENT(node), child); } } else *rootp = child; + + SET_PARENT(node, child); } static inline void -rotate_right(RBT_NODE_T node, RBT_NODE_T parent, RBT_NODE_T *rootp) { - RBT_NODE_T child; +rotate_right(RBT_NODE *node, RBT_NODE **rootp) { + RBT_NODE *child; REQUIRE(node != NULL); REQUIRE(rootp != NULL); @@ -83,27 +106,30 @@ rotate_right(RBT_NODE_T node, RBT_NODE_T parent, RBT_NODE_T *rootp) { REQUIRE(child != NULL); SET_LEFT(node, RIGHT(child)); + if (RIGHT(child) != NULL) + SET_PARENT(RIGHT(child), node); + SET_RIGHT(child, node); - if (parent != NULL) { - if (LEFT(parent) == node) - SET_LEFT(parent, child); - else { - INSIST(RIGHT(parent) == node); /* XXX remove */ - SET_RIGHT(parent, child); - } + SET_PARENT(child, PARENT(node)); + if (PARENT(node) != NULL) { + if (LEFT(PARENT(node)) == node) + SET_LEFT(PARENT(node), child); + else + SET_RIGHT(PARENT(node), child); } else *rootp = child; + + SET_PARENT(node, child); } isc_result_t -RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { - RBT_NODE_T current, child, root, parent, grandparent, tmp; +RBT_INSERT(RBT_NODE *node, RBT_NODE **rootp, int (*compare)(void *, void *)) { + RBT_NODE *current, *child, *root, *parent, *grandparent; int i; - unsigned int depth = 0; - RBT_NODE_T ancestors[MAX_DEPTH]; REQUIRE(rootp != NULL); REQUIRE(LEFT(node) == NULL && RIGHT(node) == NULL); + REQUIRE(KEY(node) != NULL); root = *rootp; if (root == NULL) { @@ -115,11 +141,8 @@ RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { current = NULL; child = root; do { - INSIST(depth < MAX_DEPTH); - ancestors[depth] = current; - depth++; current = child; - i = COMPARE_KEYS(KEY(node), KEY(current)); + i = compare(KEY(node), KEY(current)); if (i == 0) return (ISC_R_EXISTS); if (i < 0) @@ -127,17 +150,16 @@ RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { else child = RIGHT(current); } while (child != NULL); - INSIST(depth < MAX_DEPTH); - ancestors[depth] = current; if (i < 0) SET_LEFT(current, node); else SET_RIGHT(current, node); MAKE_RED(node); + SET_PARENT(node, current); - while (node != root && IS_RED(ancestors[depth])) { - parent = ancestors[depth]; - grandparent = ancestors[depth - 1]; + while (node != root && IS_RED(PARENT(node))) { + parent = PARENT(node); + grandparent = PARENT(PARENT(node)); TRACE_NODE(n); @@ -149,24 +171,18 @@ RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { MAKE_BLACK(child); MAKE_RED(grandparent); node = grandparent; - depth -= 2; } else { if (node == RIGHT(parent)) { TRACE_CASE(2); - tmp = node; - rotate_left(parent, grandparent, - &root); node = parent; - parent = tmp; - ancestors[depth] = parent; - /* Note: depth does not change. */ + rotate_left(node, &root); + parent = PARENT(node); + grandparent = PARENT(PARENT(node)); } TRACE_CASE(3); MAKE_BLACK(parent); MAKE_RED(grandparent); - INSIST(depth >= 2); /* XXX */ - tmp = ancestors[depth - 2]; - rotate_right(grandparent, tmp, &root); + rotate_right(grandparent, &root); } } else { child = LEFT(grandparent); @@ -176,24 +192,18 @@ RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { MAKE_BLACK(child); MAKE_RED(grandparent); node = grandparent; - depth -= 2; } else { if (node == LEFT(parent)) { TRACE_CASE(5); - tmp = node; - rotate_right(parent, grandparent, - &root); node = parent; - parent = tmp; - ancestors[depth] = parent; - /* Note: depth does not change. */ + rotate_right(node, &root); + parent = PARENT(node); + grandparent = PARENT(PARENT(node)); } TRACE_CASE(6); MAKE_BLACK(parent); MAKE_RED(grandparent); - INSIST(depth >= 2); /* XXX */ - tmp = ancestors[depth - 2]; - rotate_left(grandparent, tmp, &root); + rotate_left(grandparent, &root); } } } @@ -204,12 +214,192 @@ RBT_INSERT(RBT_NODE_T node, RBT_NODE_T *rootp) { return (ISC_R_SUCCESS); } -RBT_NODE_T -RBT_SEARCH(RBT_NODE_T current, RBT_KEY_T key) { +/* node must belong to a pointer in the tree; how could this be ensured? */ +isc_result_t +RBT_DELETE(RBT_NODE *delete, RBT_NODE **rootp) { + RBT_NODE *successor, *sibling, *child = NULL; + + REQUIRE(rootp != NULL); + REQUIRE(delete); + + if (LEFT(delete) == NULL) + if (RIGHT(delete) == NULL) { + if (*rootp == delete) { + /* this is the only item in the tree */ + *rootp = NULL; + return(ISC_R_SUCCESS); + } + } else + /* this node has one child, on the right */ + child = RIGHT(delete); + + else if (RIGHT(delete) == NULL) + /* this node has one child, on the left */ + child = LEFT(delete); + + else { + RBT_NODE holder, *tmp = &holder; + + /* this node has two children, so it cannot be directly + deleted. find its immediate in-order successor and + move it to this location, then do the deletion at the + old site of the successor */ + successor = RIGHT(delete); + while (LEFT(successor) != NULL) + successor = LEFT(successor); + + /* the successor cannot possibly have a left child; + if there is any child, it is on the right */ + if (RIGHT(successor)) + child = RIGHT(successor); + + /* swap the two nodes; it would be simpler to just replace + the value being deleted with that of the successor, + but this rigamarole is done so the caller has complete + control over the pointers (and memory allocation) of + all of nodes. if just the key value were removed from + the tree, the pointer to the node would would be + unchanged. */ + + /* first, put the successor in the tree location of the + node to be deleted */ + + memcpy(tmp, successor, sizeof(RBT_NODE)); + + if (LEFT(PARENT(delete)) == delete) + SET_LEFT(PARENT(delete), successor); + else + SET_RIGHT(PARENT(delete), successor); + + SET_PARENT(successor, PARENT(delete)); + + if (LEFT(delete) != NULL) + SET_PARENT(LEFT(delete), successor); + + if (RIGHT(delete) != NULL && RIGHT(delete) != successor) + SET_PARENT(RIGHT(delete), successor); + + SET_COLOR(successor, COLOR(delete)); + SET_LEFT(successor, LEFT(delete)); + SET_RIGHT(successor, RIGHT(delete)); + + /* now relink the node to be deleted into the + successor's previous tree location */ + if (PARENT(tmp) != delete) { + if (LEFT(PARENT(tmp)) == successor) + SET_LEFT(PARENT(tmp), delete); + else + SET_RIGHT(PARENT(tmp), delete); + SET_PARENT(delete, PARENT(tmp)); + } else + SET_PARENT(delete, successor); + + /* original successor node has no left */ + if (RIGHT(tmp) != NULL) + SET_PARENT(RIGHT(tmp), delete); + + SET_COLOR(delete, COLOR(tmp)); + SET_LEFT(delete, LEFT(tmp)); + SET_RIGHT(delete, RIGHT(tmp)); + + } + + + /* fix the parent chain if a non-leaf is being deleted */ + if (PARENT(delete) != NULL) { + if (LEFT(PARENT(delete)) == delete) { + SET_LEFT(PARENT(delete), child); + sibling = RIGHT(PARENT(delete)); + } else { + SET_RIGHT(PARENT(delete), child); + sibling = LEFT(PARENT(delete)); + } + + if (child != NULL) + SET_PARENT(child, PARENT(delete)); + } else { + /* this is the root being deleted, with just one child */ + SET_PARENT(child, NULL); + sibling= NULL; + *rootp = child; + } + + /* fix color violations */ + if (IS_BLACK(delete)) { + RBT_NODE *parent; + parent = PARENT(delete); + + while (child != *rootp && IS_BLACK(child)) { + /* parent = PARENT(parent_pointer); */ + + if (LEFT(parent) == child) { + sibling = RIGHT(parent); + if (IS_RED(sibling)) { + MAKE_BLACK(sibling); + MAKE_RED(parent); + rotate_left(parent, rootp); + sibling = RIGHT(parent); + } + if (IS_BLACK(LEFT(sibling)) && + IS_BLACK(RIGHT(sibling))) { + MAKE_RED(sibling); + child = parent; + } else { + if (IS_BLACK(RIGHT(sibling))) { + MAKE_BLACK(LEFT(sibling)); + MAKE_RED(sibling); + rotate_right(sibling, rootp); + sibling = RIGHT(parent); + } + SET_COLOR(sibling, COLOR(parent)); + MAKE_BLACK(parent); + MAKE_BLACK(RIGHT(sibling)); + rotate_left(parent, rootp); + child = *rootp; + } + } else { + sibling = LEFT(parent); + if (IS_RED(sibling)) { + MAKE_BLACK(sibling); + MAKE_RED(parent); + rotate_right(parent, rootp); + sibling = LEFT(parent); + } + if (IS_BLACK(LEFT(sibling)) && + IS_BLACK(RIGHT(sibling))) { + MAKE_RED(sibling); + child = parent; + } else { + if (IS_BLACK(LEFT(sibling))) { + MAKE_BLACK(RIGHT(sibling)); + MAKE_RED(sibling); + rotate_left(sibling, rootp); + sibling = LEFT(parent); + } + SET_COLOR(sibling, COLOR(parent)); + MAKE_BLACK(parent); + MAKE_BLACK(LEFT(sibling)); + rotate_right(parent, rootp); + child = *rootp; + } + } + + parent = PARENT(child); + } + + if (IS_RED(child)) + MAKE_BLACK(child); + } + + return(ISC_R_SUCCESS); +} + +RBT_NODE * +RBT_SEARCH(RBT_NODE *current, void *key, int (*compare)(void *, void *)) { int i; while (current != NULL) { - i = COMPARE_KEYS(key, KEY(current)); + i = compare(key, KEY(current)); if (i == 0) break; if (i < 0) @@ -218,26 +408,123 @@ RBT_SEARCH(RBT_NODE_T current, RBT_KEY_T key) { current = RIGHT(current); } - return (current); + return(current); } static inline void -print_tree(RBT_NODE_T root, int depth) { +print_tree(RBT_NODE *root, void (*print_key)(void *), int depth) { int i; for (i = 0; i < depth; i++) putchar('\t'); if (root != NULL) { - PRINT_KEY(KEY(root)); - printf(" (%s)\n", IS_RED(root) ? "red" : "black"); + print_key(KEY(root)); + printf(" (%s", IS_RED(root) ? "RED" : "black"); + if (root->parent) { + printf(" from "); + print_key(KEY(root->parent)); + } + printf(")\n"); depth++; - print_tree(LEFT(root), depth); - print_tree(RIGHT(root), depth); + + if (IS_RED(root) && IS_RED(LEFT(root))) + printf("** Red/Red color violation on left\n"); + print_tree(LEFT(root), print_key, depth); + + if (IS_RED(root) && IS_RED(RIGHT(root))) + printf("** Red/Red color violation on right\n"); + print_tree(RIGHT(root), print_key, depth); } else printf("NULL\n"); } void -RBT_PRINT(RBT_NODE_T root) { - print_tree(root, 0); +RBT_PRINT(RBT_NODE *root, void (*print_key)(void *)) { + print_tree(root, print_key, 0); } + +#ifdef WANT_RBTGEN_MAIN + +int compare_int(void *, void *); +void print_int(void *); + +int ints[] = { 12679, 26804, 7389, 20562, 24355, 23584, + 10713, 8094, 19071, 6732, 2709, 6058, + 3995, 3896, 15121, 1142, 2679, 26340, + 30541, 29186, +#if 0 + 6584, 13201, 21238, 30455, 6500, 30669, + 10370, 2963, 26576, 17353, 19150, 19951, + 12540, 21381, 17882, 23051, 24808, 8961, + 26022, 3047, 9108, 28221, 13874, 32643, + 25856, 12601, 894, 4319, 20780, 10229, +#endif + 0 }; + +int +compare_int(void *a, void *b) { + int i = *(int *)a, j = *(int *)b; + return(i == j ? 0 : (i < j ? -1 : 1)); +} + +void +print_int(void *num) { + int i = *(int *)num; + (void)printf("%d", i); +} + +#define KEY_VALUE(node, type) (*(type *)((node)->data)) + +void +main() { + RBT_NODE nodes[sizeof(ints)/sizeof(int)], *p, *root; + int *i, j; + + setbuf(stdout, NULL); + + printf("For any two successive numbers at the same depth, the\n"); + printf("first number (and all its descendants) should be less than\n"); + printf("the number which immediately precedes it one depth level\n"); + printf("higher, and the second number (and all its descendants)\n"); + printf("should be greater than that preceding number\n\n"); + + for (i = &ints[0], p = &nodes[0]; *i != 0; i++, p++) { + printf("inserting %d\n", *i); + KEY(p) = i; + RBT_INSERT(p, &root, compare_int); + + RBT_PRINT(root, print_int); + } + + j = 2679; + i = &j; + + printf("searching for %d ...", j); + p = RBT_SEARCH(root, i, compare_int); + if (p != NULL) { + printf("found %d\n", KEY_VALUE(p, int)); + } else { + printf("not found!\n"); + } + + j = 9999; + i = &j; + + printf("searching for %d ...", j); + p = RBT_SEARCH(root, i, compare_int); + if (p != NULL) { + printf("found %d\n", KEY_VALUE(p, int)); + } else { + printf("not found!\n"); + } + + p = &nodes[sizeof(ints) / sizeof(int) - 2]; + do { + printf("deleting %d\n", KEY_VALUE(p, int)); + RBT_DELETE(p, &root); + RBT_PRINT(root, print_int); + } while (p-- != &nodes[0]); + + exit(0); +} +#endif