Iterative AVL Trees - Deletion
In a previous article I introduced what AVL trees are, their structure and insertion. If you have not read that article, go back and read it first, as code in this article builds off of code from there.
AVL Tree's are the original self balancing binary search tree, and as with all binary search tree's - self balancing trees especially - the deletion algorithm is trickier to implement than for insertion.
Compared to Red/Black trees though, they're a breeze.
Deleting Items From a BST
Deleting a node from an AVL tree is performed in three distinct operations. It begins by searching for the node which we are going to delete, which is accomplished in the usual way, this is followed by a normal hibbard deletion on the node to be removed, after which we perform the same bottom up re-balancing that we use for insertion.
void erase(T key) {
node *x = root;
bool found = false;
while (x != nullptr) {
if (x->key == key) {
found = true;
break;
}
x = (key < x->key) ? x->left:x->right;
}
if (found) {
eraseR(x);
}
}
Notice that we're using a "found" flag to escape the search loop and proceed to an iterative hibbard deletion implementation:
void eraseR(node* h) {
node* t = h;
if (h->left == nullptr)
moveNode(h, h->right);
else if (h->right == nullptr) {
moveNode(h, h->left);
} else {
node* y = min(h->right);
if (y->parent != h) {
moveNode(y, y->right);
y->right = h->right;
y->right->parent = y;
}
moveNode(h, y);
y->left = h->left;
y->left->parent = y;
}
delete t;
balance(y);
}
The moveNode method moves the node pointed to by v into the position pointed to by u, while neatly handling our parent pointers:
void moveNode(node *u, node* v) {
if (u->parent == nullptr) {
root = v;
} else if (u == u->parent->left) {
u->parent->left = v;
} else {
u->parent->right = v;
}
if (v != nullptr) {
v->parent = u->parent;
}
}
min() is of course, the standard method for obtaining the min node of a given tree:
node* min(node* h) {
node* x = h;
while (x->left != nullptr) x = x->left;
return x;
}
As mentioned, balance() is the same as used in insertion, and covered in that article:
void balance(node* x) {
while (x && x->parent) {
node *y = x->parent;
y->height = 1 + max(height(y->left), height(y->right));
if (balanceFactor(y) < -1) {
if (balanceFactor(y->right) > 0) {
right_rotate(y->right);
}
left_rotate(y);
} else if (balanceFactor(y) > 1) {
if (balanceFactor(y->left) < 0) {
left_rotate(y->left);
}
right_rotate(y);
}
x = x->parent;
}
}
And that's it! Now that we have implemented insert, search, and erase for our AVL tree, stay tuned for the next installment in my iterative AVL tree series, where I will explain implementing forward and reverse iterators for our tree so that it can be a drop in replacement for std::map or set!!
-
Pratt Parsing: Top-Down Operator Precedence
-
Generating P-Code by AST Traversal
-
Ternary Search Tries: String Specific Ordered Symbol Tables
-
Digital Search Trees
-
Lossless Compression Part III: Huffman Coding
-
Lossless Compression Part II: The LZ77 Algorithm
-
Lossless Compression Part I: Working with Bits in a Byte Oriented World
-
Bottom Up AVL Tree: The OG Self-Balancing Binary Search Tree
-
A Different Take on Merge Sort: Binary Search Trees?
-
Deleting Entries From Open-Address Hash tables
Leave A Comment