Skip to Content
Course content

157: Implementing an AVL Tree

Click on the "Edit" button in the top corner of the screen to edit your slide content.

By now, you're comfortable with basic Binary Search Trees (BSTs). They're elegant on paper, but in the real world, they have a fatal flaw: they are completely at the mercy of the order of your data. If you're inserting random IDs, a BST works great. But if you're inserting data that's already sorted—say, a list of timestamps or alphabetically sorted usernames—your "tree" isn't a tree at all. It's just a very expensive, complicated linked list.

The Trap of the Sorted Input

Imagine we're building a system to track high scores for a game. If users join and their scores happen to be inserted in increasing order (100, 200, 300, 400), a naive BST implementation just keeps tacking new nodes onto the right child. When you eventually try to search for the score of 400, you aren't getting that sweet $O(\log n)$ performance you were promised. You're traversing every single node in the system. I've seen this crash production services because a developer assumed "average case" performance would hold, only to realize their input data was naturally sequenced.

// The "Naive" way: Standard BST Insert
Node* insert(Node* node, int key) {
    if (node == NULL) return newNode(key);
    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    return node;
}

This code is clean, sure, but it's dangerous. It doesn't care if the tree is leaning heavily to one side. To fix this, we need the tree to "notice" when it's becoming unbalanced and fix itself on the fly. That's where the AVL tree comes in.

Paying for Balance with Rotations

An AVL tree adds a bit of bookkeeping: every node now stores its own height. The rule is simple: the height difference (the balance factor) between the left and right subtrees can never be more than one. If it hits two, the tree is "heavy" on one side, and we perform a rotation.

I like to think of rotations as shifting the center of gravity. If we have a chain of 10 $\rightarrow$ 20 $\rightarrow$ 30, the node 20 becomes the new root, and 10 and 30 become its children. We've traded a few extra lines of code and a small amount of memory for a guarantee that our search time will always be logarithmic, regardless of the input order.

// The "Better" way: AVL Rotation Logic
Node* rightRotate(Node* y) {
    Node* x = y->left;
    Node* T2 = x->right;

    // Perform rotation
    x->right = y;
    y->left = T2;

    // Update heights (crucial step!)
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;

    return x; // New root of this subtree
}

The trade-off here is clear: the insert function becomes significantly more complex. You can't just drop a node in and return; you have to walk back up the recursion stack, updating heights and checking for imbalances at every single level. You're doing more work during the write operation to ensure that the read operation is blazing fast.

Where the Logic Usually Breaks

When you start implementing this, you'll likely run into the "Double Rotation" problem. A single rotation fixes a "Line" (Left-Left or Right-Right), but if your imbalance looks like a "Zig-Zag" (Left-Right), a single rotation just moves the problem around without fixing it. You have to rotate the child first to turn the zig-zag into a line, and then rotate the parent to balance the whole thing.

It feels like overkill until you realize that without these rotations, the entire mathematical promise of a binary tree evaporates. In C, you have to be especially careful with your pointer assignments during these rotations. One misplaced node->left = x and you've just created a memory leak or a circular reference that will send your program into an infinite loop the next time you try to print the tree.




📋 Practical Task

Implementing the AVL Rebalancing Logic

You are provided with a partially implemented AVL tree. The newNode, height, and max functions are already written, but the insert function is currently acting like a naive BST—it inserts the nodes but doesn't perform any balancing.

Your task is to modify the insert function to implement the AVL balancing logic. You must:

  • Update the height of each ancestor node after insertion.
  • Calculate the balance factor for the current node.
  • Implement the four rotation cases (Left-Left, Right-Right, Left-Right, and Right-Left) to ensure the tree remains balanced.

Verify your implementation by inserting the sequence 10, 20, 30, 40, 50, 25. If implemented correctly, the root of the tree should be 30, not 10.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.