Skip to content
DSA trees 9 min read

Balanced Trees & AVL: Keeping Search Trees Fast

A balanced tree is a binary search tree that actively keeps its height near log n so operations stay O(log n). The AVL tree was the first self-balancing BST: after every insert or delete it checks each node’s balance factor and performs rotations to restore balance. This page explains why balance matters and how rotations work, conceptually and in code.

We build on BST operations.

Why balance matters

A plain BST is only fast when it’s bushy. Insert values in sorted order — 1, 2, 3, 4, 5 — and you get a degenerate tree that’s really a linked list:

   (1)
      \
      (2)
         \
         (3)
            \
            (4)

Now search, insert, and delete are O(n), not O(log n). Self-balancing trees prevent this by reshaping themselves so the height stays O(log n) regardless of insertion order.

The balance factor

An AVL node is balanced when the heights of its left and right subtrees differ by at most 1. We define:

balanceFactor(node) = height(node.left) - height(node.right)

An AVL tree keeps every node’s balance factor in {-1, 0, +1}. If an insert/delete pushes a factor to +2 or -2, a rotation fixes it.

Rotations: the core operation

A rotation locally restructures three nodes to reduce height while preserving the BST ordering. There are two primitives — right rotation and left rotation — and the two double rotations are just combinations.

A right rotation around y (heavy on the left) promotes its left child x:

      y                x
     / \             /   \
    x   T3   --->   T1    y
   / \                   / \
  T1  T2               T2  T3

Notice the ordering still holds: T1 < x < T2 < y < T3 before and after.

// Assume each TreeNode also stores an int height, default 1.
int height(TreeNode* n) { return n ? n->height : 0; }
void update(TreeNode* n) {
    n->height = 1 + std::max(height(n->left), height(n->right));
}
TreeNode* rotateRight(TreeNode* y) {
    TreeNode* x  = y->left;
    TreeNode* T2 = x->right;
    x->right = y;
    y->left  = T2;
    update(y);
    update(x);
    return x; // new subtree root
}

A left rotation is the mirror image (swap every left/right).

The four imbalance cases

After inserting into the subtree of an unbalanced node, identify the shape and apply the matching fix:

  • Left-Left: rotate right.
  • Right-Right: rotate left.
  • Left-Right: rotate the left child left, then rotate right.
  • Right-Left: rotate the right child right, then rotate left.

Here is the rebalance decision sketch that ties it together:

int balanceFactor(TreeNode* n) { return n ? height(n->left) - height(n->right) : 0; }
TreeNode* rebalance(TreeNode* node) {
    update(node);
    int bf = balanceFactor(node);
    if (bf > 1) {                                  // left heavy
        if (balanceFactor(node->left) < 0)         // Left-Right
            node->left = rotateLeft(node->left);
        return rotateRight(node);                  // Left-Left
    }
    if (bf < -1) {                                 // right heavy
        if (balanceFactor(node->right) > 0)        // Right-Left
            node->right = rotateRight(node->right);
        return rotateLeft(node);                   // Right-Right
    }
    return node;                                   // already balanced
}

You call rebalance on the way back up after a recursive insert or delete.

For beginners: A rotation never breaks the BST ordering — it only changes who is the parent. Each rotation is O(1), and at most a couple are needed per update.

Complexity

OperationAVL treePlain BST (worst)
Search / insert / deleteO(log n)O(n)
RotationO(1)

Rotations add a constant factor to each update in exchange for a guaranteed O(log n) height.

Going deeper: Red-black trees balance more loosely (fewer rotations on updates) and back std::map, Java’s TreeMap, and many language libraries. See the self-balancing BSTs overview.

Next steps

Measure tree shape with tree height & depth, or test your understanding on the tree exercises.

Last updated June 25, 2026
Was this helpful?