← All Posts
DSA Series · Trees · AVL Trees · Part 3

Insertion & Deletion

We know the four rotation cases. Now let's use them inside the full AVL insert and delete algorithms. The pattern is always the same: do the normal BST operation, walk back up the tree updating heights, and rotate whenever a node's balance factor hits ±2.

AVL Insertion

The Algorithm

  1. BST insert: Walk down the tree like a normal BST insertion. Place the new node as a leaf.
  2. Walk back up: As the recursion unwinds, update each ancestor's height.
  3. Check balance: At each ancestor, compute the balance factor. If it's ±2, apply the appropriate rotation.
  4. Stop after first fix: For insertion, at most one rotation (single or double) is needed to restore balance. Once fixed, all ancestors above are also balanced.
Insert flow: down for BST insert, up for rebalancing Phase 1: Go Down Compare key at each node Go left or right Insert as leaf Same as normal BST Phase 2: Come Back Up Update height at each node Compute balance factor If bf = ±2, rotate At most 1 rotation for insert

Complete Insert Code

TreeNode* insert(TreeNode* node, int key) {
    // 1. Normal BST insert
    if (!node) return new TreeNode(key);
    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node;  // duplicate, no insert

    // 2. Update height
    node->height = 1 + max(height(node->left), height(node->right));

    // 3. Get balance factor
    int bf = balance(node);

    // 4. Four rotation cases
    // LL: left-left, straight path
    if (bf > 1 && key < node->left->key)
        return rotateRight(node);

    // RR: right-right, straight path
    if (bf < -1 && key > node->right->key)
        return rotateLeft(node);

    // LR: left-right, zig-zag
    if (bf > 1 && key > node->left->key) {
        node->left = rotateLeft(node->left);
        return rotateRight(node);
    }

    // RL: right-left, zig-zag
    if (bf < -1 && key < node->right->key) {
        node->right = rotateRight(node->right);
        return rotateLeft(node);
    }

    return node;  // balanced, no rotation needed
}

Walkthrough: Inserting 3, 2, 1

Insert 3 3 bf=0 ✓ Insert 2 3 bf=+1 2 still ok ✓ Insert 1 3 bf=+2! 2 1 LL case! right rotate 3 Balanced! 2 1 3 all bf in {-1,0,+1} ✓
Inserting 3, 2, 1 in order. The third insert triggers an LL imbalance at node 3, fixed by a right rotation.

Animation: Build an AVL Tree

Watch how inserting [30, 20, 10, 25, 28, 27] builds a balanced AVL tree. Each rebalance shows which rotation fires.

Click Step to begin.

AVL Deletion

Deletion follows the same pattern, but with two differences from insertion:

The Algorithm

  1. BST delete: Find and remove the node using standard BST deletion.
  2. Walk back up: Update heights and check balance factors at every ancestor.
  3. Rotate as needed: Each unbalanced ancestor gets the appropriate rotation. Unlike insert, we don't stop after one fix; we check every ancestor.

Deciding Which Rotation for Delete

The four cases are the same, but the detection logic uses the child's balance factor rather than comparing the deleted key:

TreeNode* deleteNode(TreeNode* node, int key) {
    // 1. Standard BST delete
    if (!node) return nullptr;
    if (key < node->key)
        node->left = deleteNode(node->left, key);
    else if (key > node->key)
        node->right = deleteNode(node->right, key);
    else {
        // Found the node to delete
        if (!node->left || !node->right) {
            TreeNode* child = node->left ? node->left : node->right;
            if (!child) { delete node; return nullptr; }
            *node = *child; delete child;
        } else {
            // In-order successor (smallest in right subtree)
            TreeNode* succ = node->right;
            while (succ->left) succ = succ->left;
            node->key = succ->key;
            node->right = deleteNode(node->right, succ->key);
        }
    }

    // 2. Update height
    node->height = 1 + max(height(node->left), height(node->right));

    // 3. Rebalance (same four cases)
    int bf = balance(node);

    if (bf > 1 && balance(node->left) >= 0)
        return rotateRight(node);
    if (bf > 1 && balance(node->left) < 0) {
        node->left = rotateLeft(node->left);
        return rotateRight(node);
    }
    if (bf < -1 && balance(node->right) <= 0)
        return rotateLeft(node);
    if (bf < -1 && balance(node->right) > 0) {
        node->right = rotateRight(node->right);
        return rotateLeft(node);
    }

    return node;
}

Insert vs Delete: Key Differences

AspectInsertDelete
Rotations neededAt most 1Up to O(log n)
Case detectionCompare inserted key with child keyCheck child's balance factor
BST operationAlways adds a leafMay replace with successor
Total timeO(log n)O(log n)
Why does delete need more rotations? When you insert a node, it adds height to one path. Fixing that one path with a single rotation is enough. When you delete a node, you remove height from one path, which can cause imbalances at multiple ancestors along the way. Each one may need its own rotation.

Walkthrough: Deleting from an AVL Tree

Starting tree: {10, 5, 20, 3, 8, 15, 25, 2}. Delete node 15.

Before: delete 15 10 5 20 3 8 15 25 2 remove 15, no rotation needed After: still balanced 10 5 20 3 8 25 2
Deleting a leaf (15) from a well-balanced tree. No rotations needed here because all balance factors stay in {-1, 0, +1}.

What's Next

We've covered the full AVL insert and delete algorithms. The final post analyzes AVL tree performance, compares it with red-black trees, and covers the patterns that come up in interviews.