feat(tree): start of balanced removal

red-black-tree
Jef Roosens 2023-01-29 21:34:44 +01:00
parent b74a4e9326
commit 1d458c68a4
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 39 additions and 27 deletions

View File

@ -124,4 +124,25 @@ void vieter_tree_node_balance_after_insert(vieter_tree_node *node) {
}
}
void vieter_tree_node_remove_balanced(vieter_tree_node *node) {}
void vieter_tree_node_remove_balanced(vieter_tree_node *node) {
// A red node can only have 0 or 2 children. The node we receive only has
// one child at most, so we know if it's red that it doesn't have any
// children. A black node that has a single (right) child can be replaced by
// this child, granted it becomes black as well. Either way, the node can be
// replaced by its right child (even if it's NULL).
if (!vieter_tree_node_get(node, vieter_tree_node_black) ||
node->children[1] != NULL) {
vieter_tree_node_set_child(
node->parent, node->children[1],
vieter_tree_node_get(node, vieter_tree_node_right));
if (node->children[1] != NULL)
vieter_tree_node_set(node->children[1], vieter_tree_node_black, true);
vieter_tree_node_free(node);
return;
}
// The complicated case is when we want to remove a black leaf
}

View File

@ -12,7 +12,8 @@ void vieter_tree_node_balance_after_insert(vieter_tree_node *node);
/*
* Remove the given node, ensuring the tree remains a valid red-black tree.
*
* @param node node to remove. This node should have at least one NULL child.
* @param node node to remove. This should have at most a single child, namely
* the right one.
*/
void vieter_tree_node_remove_balanced(vieter_tree_node *node);

View File

@ -36,6 +36,16 @@ void vieter_tree_node_set_children(vieter_tree_node *parent,
}
}
void vieter_tree_node_set_child(vieter_tree_node *parent,
vieter_tree_node *child, bool right) {
parent->children[right] = child;
if (child != NULL) {
child->parent = parent;
vieter_tree_node_set(child, vieter_tree_node_right, right);
}
}
void vieter_tree_node_replace_with_child(vieter_tree_node *to_replace,
vieter_tree_node *replacement) {
to_replace->key = replacement->key;
@ -110,40 +120,19 @@ vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root,
*out = target->data;
if (target->children[0] == NULL && target->children[1] == NULL) {
vieter_tree_node_add_child(target->parent, target->key, NULL);
vieter_tree_node_free(target);
} else if ((target->children[0] == NULL) ^ (target->children[1] == NULL)) {
vieter_tree_node *child =
target->children[0] != NULL ? target->children[0] : target->children[1];
if (target->parent != NULL) {
vieter_tree_node_add_child(target->parent, child->key, child);
vieter_tree_node_free(target);
} else {
vieter_tree_node_replace_with_child(target, child);
vieter_tree_node_free(child);
}
} else {
if (target->children[0] != NULL && target->children[1] != NULL) {
vieter_tree_node *replacement = target->children[1];
while (replacement->children[0] != NULL) {
replacement = replacement->children[0];
}
// We use replacement->key here because the right child can be NULL, so
// reading its key isn't safe. Using replacement->key however, the child
// will still get placed into the right location because of how binary
// trees work.
vieter_tree_node_add_child(replacement->parent, replacement->key,
replacement->children[1]);
target->key = replacement->key;
target->data = replacement->data;
vieter_tree_node_free(replacement);
vieter_tree_node_remove_balanced(replacement);
} else {
vieter_tree_node_remove_balanced(target);
}
return vieter_tree_ok;

View File

@ -43,6 +43,7 @@ bool vieter_tree_node_get(vieter_tree_node *node, vieter_tree_node_flag flag);
void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key,
vieter_tree_node *child);
void vieter_tree_node_set_children(vieter_tree_node *parent, vieter_tree_node **children);
void vieter_tree_node_set_child(vieter_tree_node *parent, vieter_tree_node *child, bool right);
bool vieter_tree_node_validate(vieter_tree_node *node, uint64_t passed_black_nodes, uint64_t expected_black_nodes);