1. Home
  2. Data Structures
  3. AVL Tree

AVL Tree

A binary search tree that rebalances itself with rotations, so it never becomes a slow straight line. Watch LL, RR, LR and RL rotations in 3D.

Interactive 3DAdvanced15 min readDSAUpdated

Drag to rotate · Right-drag to pan · Click, then scroll to zoom · Space play · ←→ step

What's happening

Pseudocode

    Try this in the 3D model

    • Insert 5 first. Which node becomes unbalanced, and which case is it?
    • Press Insert 10…70 in order. A normal BST would become a line of height 7. What height does the AVL tree end with?
    • Insert 25, then 22. Can you spot the double rotation (LR or RL)?
    • Rotate the camera and follow one node during a rotation — its value never changes, only its position.

    The problem with plain BSTs

    A binary search tree is fast only if it stays bushy. Insert sorted data (10, 20, 30, …) and every node goes right — the tree turns into a straight line and search becomes O(n).

    An AVL tree (named after inventors Adelson-Velsky and Landis, 1962) fixes this. After every insert or delete it checks whether the tree has become lopsided and, if so, repairs it with rotations.

    The balance factor

    For every node:

    balance factor (bf) = height(left subtree) − height(right subtree)

    An AVL tree requires bf ∈ {−1, 0, +1} for every node. The model shows each node’s bf above it. If a node reaches +2 (left-heavy) or −2 (right-heavy), it must be fixed.

    Rotations

    A rotation changes a few parent–child links so that one side gets shorter and the other taller — without breaking the BST order.

    Right rotation at node z (when it’s left-heavy):

            z                y
           / \             /   \
          y   T4    →     x     z
         / \             / \   / \
        x   T3          T1 T2 T3 T4
       / \
      T1  T2

    y moves up, z moves down to the right, and subtree T3 changes parent. A left rotation is the mirror image. Each rotation is O(1) — just a few pointer changes.

    The four cases

    Let z be the lowest unbalanced node after inserting x.

    Case Where x went Fix
    LL left child’s left subtree single right rotation at z
    RR right child’s right subtree single left rotation at z
    LR left child’s right subtree left rotation at z.left, then right rotation at z
    RL right child’s left subtree right rotation at z.right, then left rotation at z

    The double-rotation cases (LR, RL) first turn a “zig-zag” into a straight line, then fix it with a single rotation. Try them all in the model.

    Why it guarantees O(log n)

    It can be proved that an AVL tree with n nodes has height at most about 1.44 · log₂(n + 2). So search, insert and delete always run in O(log n) — no bad inputs exist. After an insertion, at most one (single or double) rotation is needed.

    Code

    class Node:
        def __init__(self, v):
            self.v, self.left, self.right, self.h = v, None, None, 1
    
    def h(n):  return n.h if n else 0
    def bf(n): return h(n.left) - h(n.right)
    def fix(n): n.h = 1 + max(h(n.left), h(n.right))
    
    def rotate_right(z):
        y = z.left
        z.left, y.right = y.right, z
        fix(z); fix(y)
        return y
    
    def rotate_left(z):
        y = z.right
        z.right, y.left = y.left, z
        fix(z); fix(y)
        return y
    
    def insert(node, v):
        if node is None:
            return Node(v)
        if v < node.v:
            node.left = insert(node.left, v)
        elif v > node.v:
            node.right = insert(node.right, v)
        else:
            return node
        fix(node)
        b = bf(node)
        if b > 1 and v < node.left.v:        # LL
            return rotate_right(node)
        if b < -1 and v > node.right.v:      # RR
            return rotate_left(node)
        if b > 1 and v > node.left.v:        # LR
            node.left = rotate_left(node.left)
            return rotate_right(node)
        if b < -1 and v < node.right.v:      # RL
            node.right = rotate_right(node.right)
            return rotate_left(node)
        return node
    
    root = None
    for v in [10, 20, 30, 40, 50, 60, 70]:
        root = insert(root, v)
    print(root.v, h(root))    # 40 3  — perfectly balanced

    AVL vs Red-Black trees

    Both are self-balancing BSTs with O(log n) operations.

    AVL Red-Black
    Balance Stricter (height ≤ 1.44 log n) Looser (height ≤ 2 log n)
    Lookups Slightly faster Slightly slower
    Inserts/deletes More rotations Fewer rotations
    Used in Databases, lookup-heavy apps C++ std::map, Java TreeMap, Linux kernel

    Common mistakes

    • Forgetting to update heights after rotating (update the lower node first, then the new root).
    • Mixing up LR and RL — check which child (and which grandchild) the new key went into.
    • Not re-linking the rotated subtree back to its parent.

    Complexity at a glance

    Case / operationTimeWhy
    SearchO(log n)Height is always about 1.44 log₂ n.
    Insert (with rebalancing)O(log n)At most one single or double rotation.
    Delete (with rebalancing)O(log n)May rotate at several levels.
    One rotationO(1)Only three pointers change.
    Extra spaceO(n)

    Quick check

    Test yourself — pick an answer to see if you got it.

    1. What is the balance factor of a node?

    2. A node has balance factor +2 and the new key went into its left child's left subtree. Which fix is needed?

    3. Why do rotations keep the BST property?

    4. Inserting 1, 2, 3, 4, 5, 6, 7 into an AVL tree gives a tree of height…

    Saved only in this browser — no account needed.
    Spotted a mistake or a bug in the 3D model?

    Report a mistake

    in AVL Tree. Thank you — every report makes the lesson better for the next reader.

    We'll also include a link to the step of the 3D model you're on and your browser type, so we can reproduce it.