You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
797 lines
20 KiB
797 lines
20 KiB
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define NVALUES 10
|
|
|
|
enum rbColor {rbBlack=1, rbRed=2};
|
|
|
|
|
|
struct element
|
|
{
|
|
size_t size;
|
|
void * ptr;
|
|
|
|
enum rbColor color;
|
|
|
|
struct element * next;
|
|
struct element * last;
|
|
|
|
struct element * parent;
|
|
struct element * left;
|
|
struct element * right;
|
|
};
|
|
|
|
struct element *
|
|
newElement(size_t size)
|
|
{
|
|
struct element * element = malloc(size + sizeof(struct element));
|
|
|
|
element->size = size;
|
|
element->ptr = element + sizeof(struct element);
|
|
|
|
element->next = NULL;
|
|
element->last = NULL;
|
|
|
|
element->color = rbRed;
|
|
element->parent = NULL;
|
|
element->left = NULL;
|
|
element->right = NULL;
|
|
|
|
return element;
|
|
}
|
|
|
|
/**
|
|
* find element in tree
|
|
*/
|
|
struct element *
|
|
findElement(struct element * tree, size_t size)
|
|
{
|
|
struct element * fitting = NULL;
|
|
|
|
while (NULL != tree) {
|
|
if (tree->size == size) {
|
|
fitting = tree;
|
|
break;
|
|
}
|
|
|
|
if (size > tree->size) {
|
|
tree = tree->right;
|
|
} else {
|
|
fitting = tree;
|
|
tree = tree->left;
|
|
}
|
|
}
|
|
|
|
return fitting;
|
|
}
|
|
|
|
/*
|
|
* function to get specific elements needed for
|
|
* rb handling, grandparent, uncle and sibbling
|
|
*/
|
|
struct element *
|
|
grandparent(struct element * node)
|
|
{
|
|
if (NULL != node && NULL != node->parent) {
|
|
return node->parent->parent;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct element *
|
|
uncle(struct element * node)
|
|
{
|
|
struct element * gp = grandparent(node);
|
|
|
|
if (NULL == gp) {
|
|
return NULL;
|
|
}
|
|
|
|
if (node->parent == gp->left) {
|
|
return gp->right;
|
|
}
|
|
|
|
return gp->left;
|
|
}
|
|
|
|
struct element *
|
|
sibling(struct element * node)
|
|
{
|
|
if (NULL == node) {
|
|
return NULL;
|
|
}
|
|
|
|
if (NULL == node->parent->left || node == node->parent->left) {
|
|
return node->parent->right;
|
|
} else {
|
|
return node->parent->left;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* tree modifications...needed for rb handling.
|
|
*/
|
|
void
|
|
rotateLeft(struct element ** tree, struct element * node)
|
|
{
|
|
struct element * rightChild = node->right;
|
|
struct element * rcLeftSub = node->right->left;
|
|
|
|
rightChild->left = node;
|
|
rightChild->parent = node->parent;
|
|
node->right = rcLeftSub;
|
|
if (NULL != rcLeftSub) {
|
|
rcLeftSub->parent = node;
|
|
}
|
|
|
|
if (node->parent) {
|
|
if (node->parent->left == node) {
|
|
node->parent->left = rightChild;
|
|
} else {
|
|
node->parent->right = rightChild;
|
|
}
|
|
} else {
|
|
*tree = rightChild;
|
|
}
|
|
|
|
node->parent = rightChild;
|
|
}
|
|
|
|
void
|
|
rotateRight(struct element ** tree, struct element * node)
|
|
{
|
|
struct element * leftChild = node->left;
|
|
struct element * lcRightSub = node->left->right;
|
|
|
|
leftChild->right = node;
|
|
leftChild->parent = node->parent;
|
|
node->left = lcRightSub;
|
|
if (NULL != lcRightSub) {
|
|
lcRightSub->parent = node;
|
|
}
|
|
|
|
if (node->parent) {
|
|
if (node->parent->left == node) {
|
|
node->parent->left = leftChild;
|
|
} else {
|
|
node->parent->right = leftChild;
|
|
}
|
|
} else {
|
|
*tree = leftChild;
|
|
}
|
|
|
|
node->parent = leftChild;
|
|
}
|
|
|
|
void
|
|
replaceNode(
|
|
struct element ** tree,
|
|
struct element * node1,
|
|
struct element * node2)
|
|
{
|
|
if (NULL != node1->parent) {
|
|
if (node1 == node1->parent->left) {
|
|
node1->parent->left = node2;
|
|
} else {
|
|
node1->parent->right = node2;
|
|
}
|
|
} else {
|
|
*tree = node2;
|
|
}
|
|
|
|
if (NULL != node2) {
|
|
node2->parent = node1->parent;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* insert element in tree
|
|
*/
|
|
struct element *
|
|
insertElement(struct element ** tree, struct element * element)
|
|
{
|
|
struct element * node = *tree;
|
|
struct element * new_node = NULL;
|
|
struct element * u;
|
|
struct element * g;
|
|
|
|
element->next = NULL;
|
|
element->last = NULL;
|
|
|
|
element->color = rbRed;
|
|
element->parent = NULL;
|
|
element->left = NULL;
|
|
element->right = NULL;
|
|
|
|
// if tree is empty it's simple... :)
|
|
if (NULL == node) {
|
|
*tree = node = new_node = element;
|
|
} else {
|
|
// normal binary tree add....
|
|
while (NULL != node) {
|
|
if (element->size < node->size) {
|
|
if (NULL == node->left) {
|
|
node->left = element;
|
|
node->left->parent = node;
|
|
new_node = node = node->left;
|
|
break;
|
|
} else {
|
|
node = node->left;
|
|
}
|
|
} else if (element->size > node->size) {
|
|
if (NULL == node->right) {
|
|
node->right = element;
|
|
node->right->parent = node;
|
|
new_node = node = node->right;
|
|
break;
|
|
} else {
|
|
node = node->right;
|
|
}
|
|
} else {
|
|
if (NULL == node->next) {
|
|
node->next = element;
|
|
node->last = element;
|
|
} else {
|
|
node->last->next = element;
|
|
node->last = element;
|
|
}
|
|
return node;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != new_node) {
|
|
/*
|
|
* handle reballancing rb style
|
|
*/
|
|
while (1) {
|
|
// case 1
|
|
if (node->parent == NULL) {
|
|
node->color = rbBlack;
|
|
// we're done.... :)
|
|
break;
|
|
}
|
|
|
|
// case 2
|
|
if (node->parent->color == rbBlack) {
|
|
// Tree is still valid ... wow, again we're done... :)
|
|
break;
|
|
}
|
|
|
|
// case 3
|
|
u = uncle(node);
|
|
g = grandparent(node);
|
|
|
|
if (u != NULL && u->color == rbRed) {
|
|
node->parent->color = rbBlack;
|
|
u->color = rbBlack;
|
|
g->color = rbRed;
|
|
|
|
node = g;
|
|
continue;
|
|
}
|
|
|
|
// case 4
|
|
if (node == node->parent->right && node->parent == g->left) {
|
|
rotateLeft(tree, node->parent);
|
|
node = node->left;
|
|
} else if (node == node->parent->left && node->parent == g->right) {
|
|
|
|
rotateRight(tree, node->parent);
|
|
node = node->right;
|
|
}
|
|
|
|
// case 5
|
|
g = grandparent(node);
|
|
|
|
node->parent->color = rbBlack;
|
|
g->color = rbRed;
|
|
|
|
if (node == node->parent->left) {
|
|
rotateRight(tree, g);
|
|
} else {
|
|
rotateLeft(tree, g);
|
|
}
|
|
|
|
// we're done..
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new_node;
|
|
}
|
|
|
|
/**
|
|
* delete element from tree
|
|
* here multiple functions are involved....
|
|
* =======================================================================
|
|
*/
|
|
/**
|
|
* find minimum of the right subtree aka leftmost leaf of right subtree
|
|
* aka left in-order successor.
|
|
* We return the parent of the element in the out argument parent.
|
|
* This can be NULL wenn calling.
|
|
*
|
|
* 2: *successor = {size = 80, ptr = 0x603ae0, color = rbRed, parent = 0x603160,
|
|
* left = 0x0, right = 0x0}
|
|
* 1: *node = {size = 70, ptr = 0x603a60, color = rbBlack, parent = 0x603070,
|
|
* left = 0x6030e0, right = 0x6031e0}
|
|
*
|
|
*/
|
|
struct element *
|
|
findInOrderSuccessor(struct element * tree)
|
|
{
|
|
struct element * node = tree->right;
|
|
|
|
while (NULL != node->left) {
|
|
node = node->left;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
struct element * deleteOneChild(struct element **, struct element *);
|
|
|
|
struct element *
|
|
deleteElement(struct element ** tree, struct element * element)
|
|
{
|
|
struct element * node = *tree;
|
|
struct element * del_node;
|
|
struct element * child;
|
|
struct element * s;
|
|
|
|
// find the relevant node and it's parent
|
|
while (NULL != node) {
|
|
|
|
if (element->size < node->size) {
|
|
node = node->left;
|
|
} else if (element->size > node->size) {
|
|
node = node->right;
|
|
} else {
|
|
if (NULL != node->next) {
|
|
if (NULL != node->parent) {
|
|
if (node == node->parent->left) {
|
|
node->parent->left = node->next;
|
|
} else {
|
|
node->parent->right = node->next;
|
|
}
|
|
} else {
|
|
*tree = node->next;
|
|
}
|
|
|
|
if (NULL != node->left) {
|
|
node->left->parent = node->next;
|
|
}
|
|
|
|
if (NULL != node->right) {
|
|
node->right->parent = node->next;
|
|
}
|
|
|
|
node->next->last = node->last;
|
|
node->next->color = node->color;
|
|
node->next->parent = node->parent;
|
|
node->next->left = node->left;
|
|
node->next->right = node->right;
|
|
|
|
return node;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// element not found
|
|
if (NULL == node) {
|
|
return node;
|
|
}
|
|
|
|
del_node = node;
|
|
|
|
// now our cases follows...the first one is the same as with
|
|
// simple binary search trees. Two non null children.
|
|
|
|
// case 1: two children
|
|
if (NULL != node->left && NULL != node->right) {
|
|
struct element * successor = findInOrderSuccessor(node);
|
|
|
|
enum rbColor tmpcolor = successor->color;
|
|
struct element * tmpparent = successor->parent;
|
|
struct element * tmpleft = successor->left;
|
|
struct element * tmpright = successor->right;
|
|
|
|
replaceNode(tree, node, successor);
|
|
|
|
successor->color = node->color;
|
|
successor->left = node->left;
|
|
successor->left->parent = successor;
|
|
// the right one might be successor...
|
|
if (node->right == successor) {
|
|
successor->right = node;
|
|
node->parent = successor;
|
|
} else {
|
|
successor->right = node->right;
|
|
node->right->parent = successor;
|
|
node->parent = tmpparent;
|
|
tmpparent->left = node;
|
|
}
|
|
|
|
node->color = tmpcolor;
|
|
node->left = tmpleft;
|
|
node->right = tmpright;
|
|
}
|
|
|
|
// Precondition: n has at most one non-null child.
|
|
child = (NULL == node->right) ? node->left : node->right;
|
|
replaceNode(tree, node, child);
|
|
|
|
// delete one child case
|
|
// TODO this is overly complex as simply derived from the function...
|
|
// maybe this can be simplified. Maybe not...check.
|
|
if (node->color == rbBlack) {
|
|
if (NULL != child && child->color == rbRed) {
|
|
child->color = rbBlack;
|
|
// done despite modifying tree itself if neccessary..
|
|
return del_node;
|
|
} else {
|
|
if (NULL != child) {
|
|
node = child;
|
|
} else {
|
|
node->color = rbBlack;
|
|
node->left = NULL;
|
|
node->right = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
return del_node;
|
|
}
|
|
|
|
// delete and rb rebalance...
|
|
while(1) {
|
|
// case 1
|
|
if (NULL == node->parent) {
|
|
// done again
|
|
break;
|
|
}
|
|
|
|
// case 2
|
|
s = sibling(node);
|
|
|
|
if (NULL != s && s->color == rbRed) {
|
|
node->parent->color = rbRed;
|
|
s->color = rbBlack;
|
|
|
|
/*
|
|
* detect which child we are...assumption
|
|
* if we are not parent->right and parent->right is not
|
|
* null we must be left, even if its set to NULL previously
|
|
*/
|
|
if (NULL != node->parent->right && node != node->parent->right) {
|
|
rotateLeft(tree, node->parent);
|
|
} else {
|
|
rotateRight(tree, node->parent);
|
|
}
|
|
}
|
|
|
|
s = sibling(node);
|
|
// case 3 / 4
|
|
if (NULL == s || ((s->color == rbBlack) &&
|
|
(NULL == s->left || s->left->color == rbBlack) &&
|
|
(NULL == s->right || s->right->color == rbBlack))) {
|
|
|
|
if (NULL != s) {
|
|
s->color = rbRed;
|
|
}
|
|
|
|
if (node->parent->color == rbBlack) {
|
|
// case 3
|
|
node = node->parent;
|
|
continue;
|
|
} else {
|
|
// case 4
|
|
node->parent->color = rbBlack;
|
|
// and done again...
|
|
break;
|
|
}
|
|
}
|
|
|
|
// case 5
|
|
if (NULL != s && s->color == rbBlack) {
|
|
// this if statement is trivial,
|
|
// due to case 2 (even though case 2 changed the sibling to a
|
|
// sibling's child,
|
|
// the sibling's child can't be red, since no red parent can
|
|
// have a red child).
|
|
//
|
|
// the following statements just force the red to be on the
|
|
// left of the left of the parent,
|
|
// or right of the right, so case 6 will rotate correctly.
|
|
if ((node == node->parent->left) &&
|
|
(NULL == s->right || s->right->color == rbBlack) &&
|
|
(NULL != s->left && s->left->color == rbRed)) {
|
|
|
|
// this last test is trivial too due to cases 2-4.
|
|
s->color = rbRed;
|
|
s->left->color = rbBlack;
|
|
|
|
rotateRight(tree, s);
|
|
} else if ((node == node->parent->right) &&
|
|
(NULL == s->left || s->left->color == rbBlack) &&
|
|
(NULL != s->right && s->right->color == rbRed)) {
|
|
// this last test is trivial too due to cases 2-4.
|
|
s->color = rbRed;
|
|
s->right->color = rbBlack;
|
|
|
|
rotateLeft(tree, s);
|
|
}
|
|
}
|
|
|
|
s = sibling(node);
|
|
// case 6
|
|
if (NULL != s) {
|
|
s->color = node->parent->color;
|
|
}
|
|
|
|
if (NULL != node && NULL != node->parent) {
|
|
node->parent->color = rbBlack;
|
|
|
|
/*
|
|
* detect which child we are...assumption
|
|
* if we are not parent->right and parent->right is not
|
|
* null we must be left, even if its set to NULL previously
|
|
*/
|
|
if (NULL != node->parent->right && node != node->parent->right) {
|
|
if (NULL != s->right) {
|
|
s->right->color = rbBlack;
|
|
}
|
|
rotateLeft(tree, node->parent);
|
|
} else {
|
|
if (NULL != s->left) {
|
|
s->left->color = rbBlack;
|
|
}
|
|
rotateRight(tree, node->parent);
|
|
}
|
|
}
|
|
|
|
// done...
|
|
break;
|
|
}
|
|
|
|
//deleteOneChild(tree, node);
|
|
|
|
return del_node;
|
|
}
|
|
|
|
|
|
void
|
|
traverse(struct element * tree, void (*cb)(struct element *, int))
|
|
{
|
|
struct element * previous = tree;
|
|
struct element * node = tree;
|
|
int depth = 1;
|
|
|
|
/*
|
|
* I think this has something like O(n+log(n)) on a ballanced
|
|
* tree because I have to traverse back the rightmost leaf to
|
|
* the root to get a break condition.
|
|
*/
|
|
while (node) {
|
|
/*
|
|
* If we come from the right so nothing and go to our
|
|
* next parent.
|
|
*/
|
|
if (previous == node->right) {
|
|
previous = node;
|
|
node = node->parent;
|
|
depth--;
|
|
continue;
|
|
}
|
|
|
|
if ((NULL == node->left || previous == node->left)) {
|
|
/*
|
|
* If there are no more elements to the left or we
|
|
* came from the left, process data.
|
|
*/
|
|
cb(node, depth);
|
|
previous = node;
|
|
|
|
if (NULL != node->right) {
|
|
node = node->right;
|
|
depth++;
|
|
} else {
|
|
node = node->parent;
|
|
depth--;
|
|
}
|
|
} else {
|
|
/*
|
|
* if there are more elements to the left go there.
|
|
*/
|
|
previous = node;
|
|
node = node->left;
|
|
depth++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
post(struct element * tree, void (*cb)(struct element *, int))
|
|
{
|
|
struct element * previous = tree;
|
|
struct element * node = tree;
|
|
int depth = 1;
|
|
|
|
/*
|
|
* I think this has something like O(n+log(n)) on a ballanced
|
|
* tree because I have to traverse back the rightmost leaf to
|
|
* the root to get a break condition.
|
|
*/
|
|
while (node) {
|
|
/*
|
|
* If we come from the right so nothing and go to our
|
|
* next parent.
|
|
*/
|
|
if ((NULL == node->left && NULL == node->right)
|
|
|| previous == node->right) {
|
|
|
|
struct element * parent = node->parent;
|
|
|
|
cb(node, depth);
|
|
|
|
previous = node;
|
|
node = parent;
|
|
depth--;
|
|
continue;
|
|
}
|
|
|
|
if ((NULL == node->left || previous == node->left)) {
|
|
/*
|
|
* If there are no more elements to the left or we
|
|
* came from the left, process data.
|
|
*/
|
|
previous = node;
|
|
|
|
if (NULL != node->right) {
|
|
node = node->right;
|
|
depth++;
|
|
} else {
|
|
node = node->parent;
|
|
depth--;
|
|
}
|
|
} else {
|
|
/*
|
|
* if there are more elements to the left go there.
|
|
*/
|
|
previous = node;
|
|
node = node->left;
|
|
depth++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void printElement(struct element * node, int depth)
|
|
{
|
|
int i;
|
|
|
|
printf("%s %010zu:%p(%02d)",
|
|
(node->color==rbRed)?"R":"B",
|
|
node->size,
|
|
node->ptr,
|
|
depth);
|
|
for (i=0; i<depth; i++) printf("-");
|
|
puts("");
|
|
|
|
node = node->next;
|
|
while (NULL != node) {
|
|
printf(" %s %010zu:%p(%02d)",
|
|
(node->color==rbRed)?"R":"B",
|
|
node->size,
|
|
node->ptr,
|
|
depth);
|
|
for (i=0; i<depth; i++) printf("-");
|
|
puts("");
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
void cleanup(struct element * node, int depth)
|
|
{
|
|
while (NULL != node) {
|
|
printf("free node: ");
|
|
printElement(node, 0);
|
|
struct element * next = node->next;
|
|
free(node);
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* =======================================================================
|
|
*/
|
|
int
|
|
main(int argc, char * argv[])
|
|
{
|
|
struct element * root = NULL;
|
|
struct element * found = NULL;
|
|
|
|
insertElement(&root, newElement(40));
|
|
insertElement(&root, newElement(50));
|
|
insertElement(&root, newElement(60));
|
|
insertElement(&root, newElement(70));
|
|
insertElement(&root, newElement(80));
|
|
insertElement(&root, newElement(45));
|
|
insertElement(&root, newElement(75));
|
|
insertElement(&root, newElement(85));
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
insertElement(&root, newElement(70));
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
found = findElement(root, 10);
|
|
if (NULL == found) {
|
|
printf("can't find segmenet of minimum size: %d\n", 10);
|
|
} else {
|
|
printElement(found, 0);
|
|
}
|
|
puts("");
|
|
|
|
found = findElement(root, 64);
|
|
if (NULL == found) {
|
|
printf("can't find segmenet of minimum size: %d\n", 64);
|
|
} else {
|
|
printElement(found, 0);
|
|
}
|
|
puts("");
|
|
|
|
found = findElement(root, 90);
|
|
if (NULL == found) {
|
|
printf("can't find segmenet of minimum size: %d\n", 90);
|
|
} else {
|
|
printElement(found, 0);
|
|
}
|
|
puts("");
|
|
|
|
free(deleteElement(&root, findElement(root, 70)));
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
insertElement(&root, newElement(80));
|
|
insertElement(&root, newElement(50));
|
|
insertElement(&root, newElement(80));
|
|
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
found = deleteElement(&root, findElement(root, 80));
|
|
printf("up to free: %p\n", found);
|
|
free(found);
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
found = deleteElement(&root, findElement(root, 50));
|
|
printf("up to free: %p\n", found);
|
|
free(found);
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
found = deleteElement(&root, findElement(root, 70));
|
|
printf("up to free: %p\n", found);
|
|
free(found);
|
|
puts("traverse");
|
|
traverse(root, printElement);
|
|
puts("");
|
|
|
|
// post(root, cleanup);
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
// vim: set et ts=4 sw=4:
|