v9.1.1
Menu

Chapter 4: Depth First Search & Trees

Week 4

Master recursive thinking and tree data structures. Learn how DFS explores paths to their deepest point before backtracking, and apply this pattern to solve classic tree problems including path sums, balanced checks, BST validation, and lowest common ancestor.

Topics
8
Demos
8
Min read
33

Chapter Overview

Trees are one of the most important data structures in computer science. They model hierarchical relationships---file systems, organizational charts, HTML documents, and decision processes all have tree-like structure. The ability to traverse and manipulate trees is fundamental to almost every area of software engineering.

Depth First Search (DFS) is the natural way to explore a tree: go as deep as possible along one branch before backtracking and trying the next. DFS maps elegantly onto recursion, making recursive thinking the key skill for this chapter.

Before diving into trees, we review recursion itself---understanding base cases, recursive cases, and how the call stack works. Then we build up from basic traversals to increasingly sophisticated tree problems: computing depth, checking balance, inverting structure, validating BST properties, and finding common ancestors.

This chapter covers:

  • Recursion fundamentals: Base cases, stack frames, and tree recursion
  • Binary tree structure: Nodes, edges, height, depth, and traversal orders
  • DFS patterns: Tracking state, returning values, and combining results from subtrees
  • Classic tree problems: Maximum depth, balanced check, inversion, BST validation, and LCA

Chapter Roadmap

Click any topic to jump in

Interactive demo
1
Recursion Basics

Base case and recursive case — the decomposition that makes every tree algorithm in this chapter possible.

Base Case and Recursive CaseStack Frames and Tree Recursion
Recursion meets trees

Trees are the most natural recursive structure — the call stack follows the tree exactly.

2
Tree Fundamentals

Nodes, children, and the three canonical traversal orders — pre-order, in-order, and post-order.

Binary Tree StructureTree Traversal Orders
3
DFS Template

Pass state down, return values up — the reusable skeleton that solves most recursive tree questions.

Passing State Down and Returning Values UpDFS Template for Trees
From traversal to template
4
Maximum Depth

A one-line DFS that returns $1 + \max(\text{left}, \text{right})$ — the simplest bottom-up computation.

Recursive Depth ComputationDepth vs Height Terminology
5
Balance Checking

Propagate height up the tree and short-circuit on imbalance — $O(n)$ versus a naive $O(n^2)$.

Bottom-Up Balance CheckingNaive vs Optimal Approach
Applying the template
6
Tree Mutation

Swap children recursively — a drop-in example of pre- vs post-order modifications.

Recursive Tree ModificationPre-order vs Post-order Modification
From reading to writing
7
BSTs

The in-order-sorted invariant enables $O(h)$ search, insert, and delete — $O(\log n)$ when balanced.

BST Property and SearchValidating BST with Range Checking
8
Lowest Common Ancestor

The canonical tree algorithm — works on any binary tree and is $O(1)$-per-node on a BST.

Recursive LCA AlgorithmLCA in a Binary Search Tree

Topics

8 topics in this chapter

A folder on your laptop contains files and more folders, which contain files and more folders, nested to an unknown depth, and you want the total size of everything inside. A loop does not fit naturally, because you do not know in advance how many levels to descend. What you do know is that a folder's size is the size of its files plus the sizes of its subfolders, and each subfolder is the same problem, only smaller.

The previous chapter, Two Pointers & Sliding Window, solved problems with a flat left-to-right sweep. Trees and nested data are not flat, and recursion is the tool that matches their shape: a function answers a small input directly and, for anything larger, calls itself on smaller pieces and combines the results. Each pending call waits on the call stack, so recursion also costs memory proportional to its depth.

We first pin down the two parts every recursive function needs, a base case and a recursive case that makes progress. Then we look at stack frames and tree recursion, where a function calls itself more than once and the number of calls can explode.

Definition

A recursive function solves a problem by calling itself on strictly smaller inputs. It needs a base case, answered directly without recursion, and a recursive case that reduces the input toward that base case and combines the results. Each active call occupies a stack frame, so the extra space used is proportional to the maximum recursion depth.

In this topic

  1. 1Base Case and Recursive Case
  2. 2Stack Frames and Tree Recursion
1 of 2
Base Case and Recursive Case

A recursive function is only correct if every chain of calls eventually stops, so it needs two parts. The base case is the smallest input, answered directly, such as factorial(1) = 1. The recursive case handles larger input by calling the function on a strictly smaller input and combining the result, such as n * factorial(n - 1). Correctness follows by induction: if the base case is right and each step is right given a correct smaller answer, every input is right. The classic failure is a base case the input can skip past, which recurses until the stack overflows.

Mathematical Intuition

A recursion terminates iff every input eventually reaches a base case along every call path. Formally, define a well-ordering ≺\prec on inputs such that each recursive call f(x′)f(x') has x′≺xx' \prec x — the well-foundedness guarantees finite descent. Without a base case, you get infinite recursion; without strict descent, you get infinite recursion in a different disguise (e.g., f(n)f(n) calling f(n)f(n)).

Example:

Write a recursive function to compute the factorial of n (n!).

2 of 2
Stack Frames and Tree Recursion

Every call gets its own stack frame holding its arguments, local variables, and the place to return to, and the frame stays on the call stack until the call returns. The deepest chain of pending calls therefore sets the memory cost: recursion depth d means O(d) space. When a function calls itself more than once, the calls form a call tree. Fibonacci, fib(n) = fib(n-1) + fib(n-2), is the standard example, and it recomputes the same subproblems again and again, so its running time grows exponentially, roughly as 1.6^n. Caching results, called memoization, collapses the tree to n distinct calls.

Mathematical Intuition

Each call adds a frame of size O(locals)O(\text{locals}). For a balanced binary tree of nn nodes, DFS has depth Θ(log⁡n)\Theta(\log n), so call-stack memory is Θ(log⁡n)\Theta(\log n). For a skewed tree (linked list shape), depth is Θ(n)\Theta(n) — which is why DFS on adversarial inputs can exhaust Python's default recursion limit of 10001000.

Example:

Write the recursive Fibonacci function and trace its call tree for fib(5).