working single-level red/black tree code
This commit is contained in:
@@ -22,8 +22,16 @@
|
||||
|
||||
#include <isc/result.h>
|
||||
|
||||
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 *));
|
||||
|
||||
407
lib/isc/rbtgen.c
407
lib/isc/rbtgen.c
@@ -33,6 +33,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <isc/rbtgen.h>
|
||||
#include <isc/boolean.h>
|
||||
#include <isc/assertions.h>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user