- James S. Plank
- Directory:
**/home/plank/cs202/Notes/AVLTree** - Original Notes:
*Mid 2000's.* - Last Revision:
*Mon Nov 8 13:00:09 EST 2021*

UNIX>That's a big problem with binary search trees. AVL trees (and other balanced trees like Splay trees, Red-Black trees, B-trees, 2-3 trees, etc) make sure that their trees are balanced so that the various operations are much faster. For example, the programwc input.txt10000 51456 770000 input.txt UNIX>head input.txtINSERT Ellie Warlike 944-867-2246 165-79-8849 INSERT David Bobble 026-631-5520 826-96-9094 INSERT Isaac Giuliano 462-055-3150 827-30-6292 INSERT Madison Fiend 193-149-4333 106-62-2934 INSERT Chloe Skew 257-554-8530 481-12-6340 INSERT Julia Postdoctoral 018-992-9715 512-23-5507 INSERT Connor Teledyne 808-602-6582 702-11-9340 INSERT Caleb Disciple 457-440-4397 076-91-9105 INSERT Avery Chloe Panther 243-649-0973 727-68-6107 INSERT Anna Placenta 193-082-7570 836-85-9844 UNIX>time sh -c "bstree_test - < input.txt"0.099u 0.006s 0:00.11 81.8% 0+0k 0+1io 0pf+0w UNIX>time bstree_test - < input.txt0.105u 0.004s 0:00.11 90.9% 0+0k 0+0io 0pf+0w UNIX>time sh -c "sort input.txt | bstree_test -"1.721u 0.014s 0:01.73 100.0% 0+0k 0+1io 0pf+0w UNIX>

UNIX>As you can see, since sorting takes .25 seconds, performing insertions with the AVL tree takes the same time when the input is sorted as when it is not sorted.time avltree_test - < input.txt0.099u 0.003s 0:00.10 90.0% 0+0k 0+0io 0pf+0w UNIX>time sh -c "sort input.txt | avltree_test -"0.336u 0.011s 0:00.34 100.0% 0+0k 0+0io 0pf+0w UNIX>time sh -c "sort input.txt > /dev/null"0.245u 0.007s 0:00.25 96.0% 0+0k 0+0io 0pf+0w UNIX>

A central operation with AVL Trees is a *rotation*. It is a way of
changing a binary search tree so that it remains a binary search tree,
but changes how it is balanced. The concept is illustrated below:

**B** and **D** are nodes in a binary search tree. They can occur anywhere
a tree, but we don't worry about what's above them -- just what's below them.
**A**, **C** and **E** are subtrees that rooted as the children of
**B** and **D**. They may be empty. If they are not empty, then since
the tree is a binary search tree, we know that:

- Every node in
**A**has a key less than**B**'s key. - Every node in
**C**has a key less than**D**'s key, but greater than**B**'s key. - Every node in
**E**has a key greater than**D**'s key.

When we perform a rotation, we perform it *about* a node. For example,
the rotation pictured above rotates about node **D** to turn the tree on the
left to the tree on the right. It also shows that you can turn the tree on the
right to the tree on the left by rotating about node **B**.

When you rotate about a node, you are going to change the tree so that the
node's parent is now the node's child. The middle subtree (subtree **C**)
will change from being one node's child to being the other node's child.
The rotation does not violate any of the properties of binary search trees.
However, it changes the shape of the tree, and there are multiple types
of trees, like AVL, Splay and Red-Black trees that
employ rotations to ensure that the trees are balanced.

Below are some example rotations. Make sure you understand all of them:

The definition of an AVL tree is follows:

- Every subtree of the tree is an AVL tree.
- For each node, the difference between the heights of its left and right subtrees is at most one.

Below are some AVL trees:

And below are two trees that are binary search trees, but are not AVL trees.

Binky violates the definition |
Fred violates the definition |

- If that node's height hasn't changed because of the insertion, then you are done. You may stop checking, and the insertion is complete.
- If the node's height has changed, but it does not violate the balance property, then you continue checking the next node in the path up to the root.
- If the node's height has changed and it now violates the balance property, then you need to perform one or two rotations to fix the problem, and then you are done.

Let's try some examples. Suppose I have the following AVL tree -- I now annotate the nodes with their heights:

If I insert *Ahmad*, take a look at the resulting tree:

The new node *Ahmad* has a height of one, and when I travel the path up to
the root, I change *Baby Daisy's* height to two. However, her node is not
imbalanced, since the height of her subtrees are 1 and 0. Moving on, *Binky's*
height is unchanged, so we can stop -- the resulting tree is indeed an AVL tree.

However, suppose I now try to insert *Waluigi*. I get the following
tree:

Traveling from the new node to the root, I see that *Fred* violates the balance
condition. Its left child is an empty tree, and as such has a height of 0.
Its right child has a height of 2.
I have to *rebalance* the tree.

Up to two of the three subtrees *A*, *C* and *E* may be empty in this picture,
but all three won't be empty. You'll note that the two pictures are mirror copies
of one another.

Now, each of these may be further broken up into two cases, which we call "Zig-Zig" and "Zig-Zag". Let's concentrate on Zig-Zig, because it is simpler. Here is what it looks like in its two mirror images:

You'll note that the defining feature is that the direction of the imbalance either goes from the right child of the root to its right child (in the left picture), or from the left child of the root to its left child (in the right picture). That's why it's called "Zig-Zig".

To fix the Zig-Zig imbalance, you rotate about the child. That "fixes" the imbalance in each case, and it also decreases the height of the tree. In the pictures below, make sure that you double-check all of the nodes to make sure they meet their balance conditions:

This is the left tree above, rotated about node D. |
This is the right tree above, rotated about node B. |

It's now a good time to do some examples where we insert a node into an AVL tree,
it becomes imbalanced due to a Zig-Zig imbalance, and we then fix it with a rotation.
What I'm going to do in each picture below is show the tree and state what node
we're inserting. Then I'll draw the resulting tree, which is imbalanced, and I'll
shade the
*A*, *C* and *E* subtrees. I'll then show the balanced tree that
results when you perform the rotation:

We insert "Ralph" |
"Khloe" is imbalanced. |
It's an AVL Tree again! |

We insert "Becca" |
"Eunice" is imbalanced. |
It's an AVL Tree again! |

We insert "Zelda" |
"Henry" is imbalanced. |
It's an AVL Tree again! |

One important thing to note is that after the rebalancing, the entire AVL tree is balanced -- the height of the rebalanced subtree is the same after rebalancing than it was before the node was inserted.

The "Zig-Zag" imbalance happens when the imbalance goes right, then left, or left, then right. Here's what it looks like in its two mirror images:

To explain how to fix this, we need to blow up the *C* tree in the picture
above, relabeling the nodes and subtrees so that they make sense:

To rebalance the Zig-Zag case, we need to rotate twice about the grandchild.
In each of these pictures, the grandchild is *D*. In the pictures
below, we rotate once about *D*, but the tree is not balanced yet:

One rotation about |
One rotation about |

We perform one more rotation about *D*, and now the tree is balanced.
In fact, in both cases, the resulting trees are identical!

After the second rotation about |
After the second rotation about |

Let's do some examples:

We insert "Don". |
"Eve" is imbalanced. |
It's an AVL Tree again! |

We insert "Eve". |
"Kim" is imbalanced. |
It's an AVL Tree again! |

We insert "Ginger". |
"Brad" is imbalanced. |
It's an AVL Tree again! |

- You insert a node into an AVL tree just like you insert a node into a regular binary search tree.
- After inserting, you need to travel from this new node up to the root, and potentially increment the height of the nodes.
- You also need to check to see if a node is imbalanced. If it is, you need to rebalance.
- To rebalance, you need to identify whether it is a Zig-Zig or a Zig-Zag imbalance.
- If Zig-Zig, then you rotate once about the child of the imbalanced node.
- If Zig-Zag, then you rotate twice about the grandchild of the imbalanced node.
- After rebalancing, you are done.

- Check to see if node is balanced. If it is, you may need to decrement its height.
- If it is imbalanced, then you need to identify whether it is Zig-Zig or Zig-Zag.
- You treat Zig-Zig and Zig-Zag as described above, performing one or two rotations to rebalance the tree.

Zig-Zig -- same as above. |
This case only occurs with deletion. We treat it as a Zig-Zig. |
Zig-Zag -- same as above. |

After rebalancing, you can't stop, as you do with insertion. Instead you need to keep traveling toward the root. You only stop when you reach the root, or you don't change the height of a node (because then the heights of its ancestor nodes won't change either).

Let's look at some examples. As with insertion, I'll show an original tree before deletion, the tree after deletion, but before rebalancing, and the tree after rebalancing.

We delete "Hal". |
We check Hal's parent, Ian, and it's balanced and its height is unchanged. We're done. |

We delete "Ian". |
As we move up to the root, we see that "Cal" is imbalanced. It's a Zig-Zig, so we rotate about "Bob". |
It's an AVL Tree again. You'll note we still had to travel up from Bob to the root, and change Kim's height from 5 ato 4. |

We delete "Nell". To do that, we find the largest node in Nell's left subtree -- Anne. We delete Anne and replace Nell with Anne. |
Anne is imbalanced. It's a Zig-Zag, so we rotate twice about "Omar". |
It's an AVL Tree again. |

We delete "Naomi". To do that, we find the largest node in Naomi's left subtree -- Jet. We delete Jet and replace Naomi with Jet. |
Jet is imbalanced. It's a Zig-Zig, so we rotate about "Samson". |

That subtree is balanced, but have to continue going to the root. We find that "Henry" is also imbalanced, and that it's a Zig-Zag. Two rotations about ""Dub". |
Now, we're done and the tree is balanced. |

- Inserting into an AVL tree with
*n*nodes:*O(log n)*. - Finding an item in an AVL tree with
*n*nodes:*O(log n)*. - Deleting from AVL tree with
*n*nodes:*O(log n)*. - Creating an AVL tree that has
*n*nodes:*O(n log n)*. - Performing any of those recursive traversals on an AVL tree with
*n*nodes:*O(n)*.