Currently available · INDIA
← Writing
Algorithms · 8/8·Sep 8, 2026·8 min read

Binary Trees & BSTs — Complete Guide with Interactive Walkthroughs

Binary trees, BST search and insert, DFS vs BFS, inorder, validate, and LCA — with step-through animations and a practice ladder.

0

A binary tree is a hierarchy: each node holds a value and up to two children — left and right. That shape shows up in file systems, expression parsers, and half of medium interview problems.

A binary search tree (BST) adds one rule: every value in the left subtree is less than the node; every value on the right is greater. Search and insert become comparisons that discard half the tree when the tree stays balanced — the same idea as Binary Search, but the “array” is linked by pointers.

This guide continues after Stacks & Queues and Linked Lists. Queues power level-order traversal; linked-list pointer care maps directly onto left / right.

Core Patterns

PatternShapeWhen to use
DFS recursionleft / right recurseDepth, paths, validate, invert, diameter
BFS / level orderqueue of nodesLevel averages, zigzag, shortest tree path
BST search / insertcompare vs node.valOrdered tree — O(h) lookup and insert
Traversalspre / in / postSerialize, rebuild, sorted order from BST
LCA / ancestorspath or BST boundsLowest common ancestor, path between nodes

Anatomy of a Node

javascript
class TreeNode {
  val
  left  → TreeNode | null
  right → TreeNode | null
}

The root is the only node you are given. Lose it and you lose the tree — same discipline as keeping the head of a list.

Height / depth: longest root-to-leaf path (counting nodes or edges — pick one definition and stick to it). Interview code usually returns node count: a single node has depth 1.

Leaf: both children null.

Binary Tree vs Sorted Array

Binary tree / BSTSorted array
LookupO(h) — O(log n) if balancedO(log n) binary search
Insert / deleteO(h) with local pointer updatesO(n) shift to keep sorted
Sorted iterationInorder walk — O(n)Already sorted — O(n)
Hierarchy / ancestorsNatural parent–child pathsNo structure — rebuild indices
Prefer whenMany inserts + ordered queriesStatic data, heavy random access

Use a BST when you need ordered inserts and deletes without shifting an array. Use a sorted array when the data is mostly static and you lean on binary search.

Compare the target to the current node. Go left or right. Stop on match or null.

BST Search

Search for 6 — compare and discard half the tree

831016144713
Step 1/3target = 6

Start at root 8

6 < 8 → go left

function searchBST(root, val) {
  let cur = root;
  while (cur) {
    if (cur.val === val) return cur;
    cur = val < cur.val ? cur.left : cur.right;
  }
  return null;
}

Time is O(h). Balanced ≈ O(log n). A linked-list-shaped tree is O(n) — that is why production trees rebalance (AVL, red-black, B-trees).

BST Insert

Walk like search. When you hit a null child slot, attach the new node there.

BST Insert

Insert 5 — walk until a null child, then attach

831016144713
Step 1/4insert 5

Start at 8

5 < 8 → left

function insertIntoBST(root, val) {
  if (!root) return new TreeNode(val);

  if (val < root.val) root.left = insertIntoBST(root.left, val);
  else root.right = insertIntoBST(root.right, val);

  return root;
}

Traversals

OrderVisit sequenceClassic use
Preordernode → left → rightSerialize / clone shape
Inorderleft → node → rightSorted values from a BST
Postorderleft → right → nodeDelete tree, evaluate expression
Level orderBFS by depthLevel averages, zigzag

Inorder Traversal

Left → node → right — on a BST this visits values sorted

831016144713

Visit order: 1

Step 1/9left → node → right

Visit 1

Leftmost leaf first

Inorder on a BST prints values in sorted order. That is the cheapest mental check for “is this a BST?” — and the seed of the inorder-validation trick.

Maximum Depth (DFS)

javascript
depth(null) = 0
depth(node) = 1 + max(depth(left), depth(right))

Maximum Depth

depth(node) = 1 + max(depth(left), depth(right))

538149
Step 1/6

Leaf 1 → depth 1

Base case: null child contributes 0

function maxDepth(root) {
  if (!root) return 0;
  return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

The same skeleton solves invert tree (swap children after recursing), same tree (compare pairs of nodes), and diameter (track longest path while returning height).

Level Order (BFS)

Put the root in a queue. For each level, dequeue everyone currently in the queue, enqueue their children. Pair with Stacks & Queues — this is the queue pattern on a tree.

Level-Order BFS

Queue processes one level at a time

538149

Visit order: 5

Step 1/3queue

Level 0: dequeue 5

Enqueue children 3, 8

function levelOrder(root) {
  if (!root) return [];
  const out = [];
  const q = [root];

  while (q.length) {
    const size = q.length;
    const level = [];
    for (let i = 0; i < size; i++) {
      const node = q.shift();
      level.push(node.val);
      if (node.left) q.push(node.left);
      if (node.right) q.push(node.right);
    }
    out.push(level);
  }

  return out;
}

In hot paths, prefer a deque / index pointer over shift so front removal stays O(1).

Validate BST

Comparing each node only to its parent is not enough. A classic counterexample: 5 with left child 1, and 1 has right child 6. Parent checks pass; BST rule fails — 6 sits in the left subtree of 5.

Pass (lo, hi) bounds down the recursion. Left child inherits (lo, node.val); right inherits (node.val, hi).

Validate BST

Each node must stay inside (lo, hi) bounds from ancestors

5176
Step 1/3validate

Root 5 — bounds (−∞, +∞)

Left subtree must be < 5; right must be > 5

function isValidBST(root) {
  function ok(node, lo, hi) {
    if (!node) return true;
    if (node.val <= lo || node.val >= hi) return false;
    return ok(node.left, lo, node.val) && ok(node.right, node.val, hi);
  }
  return ok(root, -Infinity, Infinity);
}

Alternate: inorder walk and ensure each value is strictly greater than the previous.

Lowest Common Ancestor (BST)

In a BST, walk from the root:

  • If both values are less than the node → go left
  • If both are greater → go right
  • Otherwise this node is the split — the LCA

No need to build full parent paths when the ordering holds.

Complexity Cheat Sheet

ProblemTimeSpace
DFS over all nodesO(n)O(h)
Level-order BFSO(n)O(w)
BST search / insertO(h)O(1)*
Balanced BST (AVL / red-black)O(log n)O(log n)
Skewed tree (worst h = n)O(n)O(n)

h = height, w = max width of a level. * Iterative BST search; recursive uses O(h) stack.

Common Mistakes

Forgetting the null base case — every recursive tree function starts with if (!node) return ….

Parent-only BST checks — use bounds or inorder.

Mutating during traversal without a plan — invert is fine (swap after recurse); deleting while iterating needs care.

Assuming balance — interview BSTs can be skewed unless the problem says “balanced.”

Confusing preorder / inorder / postorder when rebuilding from two arrays — the first preorder element is the root; inorder splits left/right sizes.

Practice Problems (Easiest → Hardest)

Quick Reference — Pattern Picker

Pattern Picker

Click a node to trace the path — cards below update with your selection

orderedby depthrecurselookupcheckqueueancestorsarraysTree problem?Is it a BST?Need levels?DFS recurseSearch / insertValidate BSTBFS queueLCA / pathRebuild tree

Key Takeaways

  1. A binary tree is val + left + right. A BST adds the ordering invariant.
  2. DFS recursion is the default template for depth, invert, diameter, and path problems.
  3. BFS + queue owns level-order and “by depth” questions.
  4. BST search / insert is O(h) — balance decides whether that is log or linear.
  5. Validate BST with (lo, hi) bounds or a strictly increasing inorder pass.
  6. LCA in a BST is a single walk: diverge left/right until the split node.

Next in the series: heaps — priority queues built on complete binary trees, where parent–child order replaces the full BST invariant.

/ Stay in the loop

Get the next one in
your inbox.

New essays and field notes sent only when there's something worth sending. No tracking, no spam, easy unsubscribe.

Join · No spam · One-click unsubscribe