{
  "id": "trees/avl-tree",
  "version": 1,
  "deterministic": true,
  "url": "/dsa/trees/avl-tree",
  "topic": {
    "slug": "trees",
    "title": "Trees"
  },
  "title": "AVL Tree",
  "tagline": "A search tree that refuses to become a list — it rotates itself level again.",
  "vizKind": "tree",
  "complexity": {
    "time": {
      "best": "O(log n)",
      "average": "O(log n)",
      "worst": "O(log n)"
    },
    "space": "O(n)",
    "growth": "log",
    "note": "O(log n) search, insert and delete — GUARANTEED, in the worst case. That's the whole point: a plain BST is O(log n) only if you're lucky with the input order. AVL removes the luck, at the cost of a few rotations."
  },
  "code": {
    "javascript": "function insert(node, value) {\n  if (node === null) return new Node(value);\n\n  if (value < node.value) node.left = insert(node.left, value);\n  else node.right = insert(node.right, value);\n\n  update(node);                       // recompute height\n  return rebalance(node);             // fix it if it tipped over\n}\n\nfunction rebalance(node) {\n  const bf = height(node.left) - height(node.right);\n\n  if (bf > 1) {                       // too heavy on the LEFT\n    if (height(node.left.left) < height(node.left.right))\n      node.left = rotateLeft(node.left);   // left-right case\n    return rotateRight(node);\n  }\n  if (bf < -1) {                      // too heavy on the RIGHT\n    if (height(node.right.right) < height(node.right.left))\n      node.right = rotateRight(node.right); // right-left case\n    return rotateLeft(node);\n  }\n  return node;                        // balanced enough\n}",
    "python": "def insert(node, value):\n    if node is None: return Node(value)\n\n    if value < node.value: node.left = insert(node.left, value)\n    else: node.right = insert(node.right, value)\n\n    update(node)                        # recompute height\n    return rebalance(node)              # fix it if it tipped over\n\n\ndef rebalance(node):\n    bf = height(node.left) - height(node.right)\n\n    if bf > 1:                          # too heavy on the LEFT\n        if height(node.left.left) < height(node.left.right):\n            node.left = rotate_left(node.left)    # left-right case\n        return rotate_right(node)\n\n    if bf < -1:                         # too heavy on the RIGHT\n        if height(node.right.right) < height(node.right.left):\n            node.right = rotate_right(node.right) # right-left case\n        return rotate_left(node)\n\n    return node                         # balanced enough\n",
    "java": "Node insert(Node node, int value) {\n  if (node == null) return new Node(value);\n\n  if (value < node.value) node.left = insert(node.left, value);\n  else node.right = insert(node.right, value);\n\n  update(node);                       // recompute height\n  return rebalance(node);             // fix it if it tipped over\n}\n\nNode rebalance(Node node) {\n  int bf = height(node.left) - height(node.right);\n\n  if (bf > 1) {                       // too heavy on the LEFT\n    if (height(node.left.left) < height(node.left.right))\n      node.left = rotateLeft(node.left);    // left-right case\n    return rotateRight(node);\n  }\n  if (bf < -1) {                      // too heavy on the RIGHT\n    if (height(node.right.right) < height(node.right.left))\n      node.right = rotateRight(node.right); // right-left case\n    return rotateLeft(node);\n  }\n  return node;                        // balanced enough\n}",
    "cpp": "Node* insert(Node* node, int value) {\n  if (!node) return new Node(value);\n\n  if (value < node->value) node->left = insert(node->left, value);\n  else node->right = insert(node->right, value);\n\n  update(node);                       // recompute height\n  return rebalance(node);             // fix it if it tipped over\n}\n\nNode* rebalance(Node* node) {\n  int bf = height(node->left) - height(node->right);\n\n  if (bf > 1) {                       // too heavy on the LEFT\n    if (height(node->left->left) < height(node->left->right))\n      node->left = rotateLeft(node->left);    // left-right case\n    return rotateRight(node);\n  }\n  if (bf < -1) {                      // too heavy on the RIGHT\n    if (height(node->right->right) < height(node->right->left))\n      node->right = rotateRight(node->right); // right-left case\n    return rotateLeft(node);\n  }\n  return node;                        // balanced enough\n}"
  },
  "defaultInput": [
    10,
    20,
    30,
    40,
    50,
    25
  ],
  "defaultTarget": null,
  "inputHint": "Deliberately sorted — a plain BST would build a straight line. Watch this one rotate.",
  "pitfalls": [
    {
      "wrong": "Forgetting to update heights on the way back up the recursion.",
      "right": "The balance factor is computed from heights. Stale heights mean the tree thinks it's balanced when it isn't, and it silently stops rebalancing."
    },
    {
      "wrong": "Missing the left-right and right-left cases.",
      "right": "If the heavy child leans the *other* way, a single rotation doesn't fix it — you must rotate the child first. Skip this and one rotation just tips the tree the other way."
    },
    {
      "wrong": "Worrying that rotating breaks the search order.",
      "right": "It can't. A rotation is carefully designed so smaller stays left and bigger stays right. That's precisely why it's a legal move."
    },
    {
      "wrong": "Using AVL when you write far more than you read.",
      "right": "AVL keeps the tree very strictly balanced, which means more rotations. A red-black tree balances more loosely — slightly slower reads, noticeably cheaper writes."
    }
  ],
  "quiz": [
    {
      "q": "What is a node's balance factor?",
      "options": [
        "Its value minus its parent's",
        "The height of its left subtree minus the height of its right subtree",
        "The number of its children",
        "Its depth in the tree"
      ],
      "answer": 1,
      "why": "left height − right height. An AVL tree allows −1, 0 or +1. The instant it reaches ±2, that subtree has tipped and must be rotated."
    },
    {
      "q": "Does a rotation break the binary-search-tree ordering?",
      "options": [
        "Yes — you have to re-sort afterwards",
        "No — smaller stays left and bigger stays right, always",
        "Only for the left rotation",
        "Only if there are duplicates"
      ],
      "answer": 1,
      "why": "A rotation re-shapes the tree while preserving the ordering exactly. That's the whole reason it's a legal repair — you can do it any time, for free, without breaking search."
    },
    {
      "q": "You insert 10, 20, 30, 40, 50 in order. What does a plain BST do, and what does an AVL do?",
      "options": [
        "Both build a balanced tree",
        "The BST builds a straight line of height 5; the AVL rotates and stays about 3 tall",
        "Both build a straight line",
        "The AVL refuses the input"
      ],
      "answer": 1,
      "why": "Sorted input is a plain BST's worst case — every value goes right, and it degenerates into a linked list. The AVL notices the tipping and rotates, keeping the height logarithmic."
    }
  ],
  "problems": [
    {
      "name": "Balance a Binary Search Tree",
      "difficulty": "Medium",
      "url": "https://leetcode.com/problems/balance-a-binary-search-tree/"
    },
    {
      "name": "Balanced Binary Tree",
      "difficulty": "Easy",
      "url": "https://leetcode.com/problems/balanced-binary-tree/"
    },
    {
      "name": "Convert Sorted Array to Binary Search Tree",
      "difficulty": "Easy",
      "url": "https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/"
    }
  ],
  "trace": {
    "note": "Each frame is one immutable step: data snapshot, pointers, per-index roles, executing code line, and a one-sentence explanation.",
    "length": 15,
    "frames": [
      {
        "data": {
          "nodes": [],
          "rootId": null
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 1,
        "variables": {},
        "explanation": "A plain search tree fed sorted values becomes a straight line, and search degrades to O(n). An AVL tree watches its own balance and rotates itself level again. The default input here is sorted on purpose — so you can watch it refuse to become a list.",
        "stats": {
          "comparisons": 0,
          "swaps": 0
        },
        "accent": "active"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null,
              "role": "active"
            }
          ],
          "rootId": "a0-10"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 10,
          "height": 1,
          "rotations": 0
        },
        "explanation": "10 is in. The tree is 1 levels tall. A plain BST fed these same sorted values would be 1 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 0,
          "swaps": 0
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": "a1-20",
              "role": "active"
            },
            {
              "id": "a1-20",
              "value": 20,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a0-10"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 20,
          "height": 2,
          "rotations": 0
        },
        "explanation": "20 is in. The tree is 2 levels tall. A plain BST fed these same sorted values would be 2 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 1,
          "swaps": 0
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": "a1-20",
              "role": "compare"
            },
            {
              "id": "a1-20",
              "value": 20,
              "left": null,
              "right": "a2-30"
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a0-10"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 13,
        "variables": {
          "at": 10,
          "balanceFactor": -2
        },
        "explanation": "10 has a balance factor of -2 — one side is 2 levels taller than the other. Anything beyond ±1 means the tree has tipped. Rotate.",
        "stats": {
          "comparisons": 3,
          "swaps": 0
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a0-10"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 23,
        "variables": {
          "newRoot": 20,
          "rotations": 1
        },
        "explanation": "Rotate left around 10. 20 rises, 10 sinks left. Level again — and the search-tree ordering is untouched.",
        "stats": {
          "comparisons": 3,
          "swaps": 1
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a2-30",
              "role": "active"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 30,
          "height": 2,
          "rotations": 1
        },
        "explanation": "30 is in. The tree is 2 levels tall after 1 rotation. A plain BST fed these same sorted values would be 3 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 3,
          "swaps": 1
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a2-30",
              "role": "active"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": "a3-40"
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 40,
          "height": 3,
          "rotations": 1
        },
        "explanation": "40 is in. The tree is 3 levels tall after 1 rotation. A plain BST fed these same sorted values would be 4 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 5,
          "swaps": 1
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a2-30"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": "a3-40",
              "role": "compare"
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": null,
              "right": "a4-50"
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 13,
        "variables": {
          "at": 30,
          "balanceFactor": -2
        },
        "explanation": "30 has a balance factor of -2 — one side is 2 levels taller than the other. Anything beyond ±1 means the tree has tipped. Rotate.",
        "stats": {
          "comparisons": 8,
          "swaps": 1
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a2-30"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 23,
        "variables": {
          "newRoot": 40,
          "rotations": 2
        },
        "explanation": "Rotate left around 30. 40 rises, 30 sinks left. Level again — and the search-tree ordering is untouched.",
        "stats": {
          "comparisons": 8,
          "swaps": 2
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a3-40",
              "role": "active"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": "a2-30",
              "right": "a4-50"
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": null,
              "right": null
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 50,
          "height": 3,
          "rotations": 2
        },
        "explanation": "50 is in. The tree is 3 levels tall after 2 rotations. A plain BST fed these same sorted values would be 5 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 8,
          "swaps": 2
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a3-40",
              "role": "compare"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": "a2-30",
              "right": "a4-50"
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": "a5-25",
              "right": null
            },
            {
              "id": "a5-25",
              "value": 25,
              "left": null,
              "right": null
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 13,
        "variables": {
          "at": 20,
          "balanceFactor": -2
        },
        "explanation": "20 has a balance factor of -2 — one side is 2 levels taller than the other. Anything beyond ±1 means the tree has tipped. Rotate.",
        "stats": {
          "comparisons": 11,
          "swaps": 2
        },
        "accent": "compare"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a2-30",
              "role": "pivot"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a2-30",
              "value": 30,
              "left": "a5-25",
              "right": "a3-40"
            },
            {
              "id": "a5-25",
              "value": 25,
              "left": null,
              "right": null
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": null,
              "right": "a4-50"
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 22,
        "variables": {
          "rotations": 3
        },
        "explanation": "A right-left case: the right child leans left. Rotate the child right first, to turn this into the simple right-right case.",
        "stats": {
          "comparisons": 11,
          "swaps": 3
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a5-25"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a5-25",
              "value": 25,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a1-20"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 23,
        "variables": {
          "newRoot": 30,
          "rotations": 4
        },
        "explanation": "Rotate left around 20. 30 rises, 20 sinks left. Level again — and the search-tree ordering is untouched.",
        "stats": {
          "comparisons": 11,
          "swaps": 4
        },
        "accent": "swap"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a2-30",
              "value": 30,
              "left": "a1-20",
              "right": "a3-40",
              "role": "active"
            },
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a5-25"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a5-25",
              "value": 25,
              "left": null,
              "right": null
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": null,
              "right": "a4-50"
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a2-30"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 2,
        "variables": {
          "inserted": 25,
          "height": 3,
          "rotations": 4
        },
        "explanation": "25 is in. The tree is 3 levels tall after 4 rotations. A plain BST fed these same sorted values would be 6 levels tall by now — a straight line.",
        "stats": {
          "comparisons": 11,
          "swaps": 4
        },
        "accent": "found"
      },
      {
        "data": {
          "nodes": [
            {
              "id": "a2-30",
              "value": 30,
              "left": "a1-20",
              "right": "a3-40"
            },
            {
              "id": "a1-20",
              "value": 20,
              "left": "a0-10",
              "right": "a5-25"
            },
            {
              "id": "a0-10",
              "value": 10,
              "left": null,
              "right": null
            },
            {
              "id": "a5-25",
              "value": 25,
              "left": null,
              "right": null
            },
            {
              "id": "a3-40",
              "value": 40,
              "left": null,
              "right": "a4-50"
            },
            {
              "id": "a4-50",
              "value": 50,
              "left": null,
              "right": null
            }
          ],
          "rootId": "a2-30"
        },
        "pointers": [],
        "highlights": [],
        "codeLine": 9,
        "variables": {
          "nodes": 6,
          "height": 3,
          "rotations": 4,
          "plainBstHeight": 6
        },
        "explanation": "6 sorted values in, and the tree is only 3 levels tall — not 6. That cost 4 rotations, and it guarantees O(log n) search forever. That guarantee is the entire reason self-balancing trees exist.",
        "stats": {
          "comparisons": 11,
          "swaps": 4
        },
        "accent": "sorted"
      }
    ]
  }
}