🌲

Binary Tree

DFS, BFS, height, diameter, and path problems on binary trees.

15 Problems3-4 hours9 Easy4 Medium2 Hard

Overview

A binary tree has nodes with at most two children. Most tree problems use DFS (preorder/inorder/postorder recursion) or BFS (level order with a queue). The key mindset is: define what your recursive function returns, trust it for subtrees, and combine results at the current node.

Complexity Summary

Time Complexity

DFS/BFS O(n)

Space Complexity

O(h) recursion stack, O(n) for BFS queue

Key Patterns & Techniques

Learn the core patterns in this topic. Each block explains when to use the pattern, the intuition behind it, and a compact code example.

1

Trust Recursion

Concept

Use this as the core mindset for every tree DFS problem

Why It Works

Define precisely what your function returns for a node, trust that it returns the correct answer for any subtree, then combine the left and right results at the current node. Never try to manually trace deep recursion; instead focus on the base case (null node) and the recurrence.

Pattern Example
Problem

Problem Maximum Depth of Binary Tree. The depth of a node is 1 plus the max depth of its two subtrees. The base case: null returns 0.

Implementation
// Trust recursion — Maximum Depth of Binary Tree
function maxDepth(root) {
  if (!root) return 0;
  return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
Practice questions for this pattern
2

Preorder DFS (Root First)

Concept

Use this when you need to process a node before its children, such as when constructing a path, serializing a tree, or copying the structure

Why It Works

Preorder visits root, then recursively visits left and right.

Pattern Example
Problem

Problem Path Sum II. At each node, add the value to the current path and recurse into children; pop after each recursive call to backtrack.

Implementation
// Preorder DFS — Path Sum II (root-to-leaf paths)
function pathSum(root, target) {
  const result = [];
  function dfs(node, remaining, path) {
    if (!node) return;
    path.push(node.val);
    if (!node.left && !node.right && remaining === node.val)
      result.push([...path]);
    dfs(node.left, remaining - node.val, path);
    dfs(node.right, remaining - node.val, path);
    path.pop();
  }
  dfs(root, target, []);
  return result;
}
Practice questions for this pattern
3

Inorder DFS (Left Root Right)

Concept

Use this when the sorted order of values matters, since inorder traversal of a BST yields values in ascending order

Why It Works

It is also useful for in-place processing where you need to see smaller nodes first.

Pattern Example
Problem

Problem Kth Smallest Element in a BST. Use inorder traversal and decrement a counter; return the value when the counter hits zero.

Implementation
// Inorder DFS — Kth Smallest Element in a BST
function kthSmallest(root, k) {
  let count = 0, result = null;
  function inorder(node) {
    if (!node || result !== null) return;
    inorder(node.left);
    if (++count === k) { result = node.val; return; }
    inorder(node.right);
  }
  inorder(root);
  return result;
}
Practice questions for this pattern
4

Postorder DFS (Left Right Root)

Concept

Use this when you need information from both children before you can process the current node, such as computing height, diameter, or checking balance.

Pattern Example
Problem

Problem Diameter of Binary Tree. Compute the height of the left and right subtrees in postorder; the diameter at this node is their sum; update the global max.

Implementation
// Postorder DFS — Diameter of Binary Tree
function diameterOfBinaryTree(root) {
  let maxDiam = 0;
  function height(node) {
    if (!node) return 0;
    const l = height(node.left), r = height(node.right);
    maxDiam = Math.max(maxDiam, l + r);
    return 1 + Math.max(l, r);
  }
  height(root);
  return maxDiam;
}
Practice questions for this pattern
5

Global Variable for Cross-Root Paths

Concept

Use this when the optimal answer may span from the left subtree through the root to the right subtree and therefore cannot be passed up the call stack in a single return value

Why It Works

Keep an external variable that you update inside the recursive function while returning the best single-branch value upward.

Pattern Example
Problem

Problem Binary Tree Maximum Path Sum. At each node, the cross-root path is node.val + max(left, 0) + max(right, 0); update the global max, but return only node.val + max(left, right, 0) to the parent.

Implementation
// Global variable — Binary Tree Maximum Path Sum
function maxPathSum(root) {
  let best = -Infinity;
  function dfs(node) {
    if (!node) return 0;
    const l = Math.max(dfs(node.left), 0);
    const r = Math.max(dfs(node.right), 0);
    best = Math.max(best, node.val + l + r);
    return node.val + Math.max(l, r);
  }
  dfs(root);
  return best;
}
Practice questions for this pattern
6

Construct Tree from Traversals

Concept

Use this when you are given preorder (or postorder) and inorder arrays and must rebuild the tree

Why It Works

The first element of preorder is always the root; find its index in the inorder array to split left and right subtrees. Recurse with correctly sized subarrays.

Pattern Example
Problem

Problem Construct Binary Tree from Preorder and Inorder Traversal. Root is preorder[0]; inorder index idx splits the array into left (size idx) and right subtrees.

Implementation
// Construct tree — Build from Preorder and Inorder Traversal
function buildTree(preorder, inorder) {
  if (!preorder.length) return null;
  const rootVal = preorder[0];
  const idx = inorder.indexOf(rootVal);
  const root = new TreeNode(rootVal);
  root.left = buildTree(preorder.slice(1, idx + 1), inorder.slice(0, idx));
  root.right = buildTree(preorder.slice(idx + 1), inorder.slice(idx + 1));
  return root;
}
Practice questions for this pattern
DSA Practice Online - 150+ Coding Interview Questions | LeetCode Alternative | InstaMock - AI Mock Interview