Module 7: Binary Trees & Binary Search Trees·Tutorial 12 of 23 (Step 7.1)
All Tutorials·DSA Roadmap·4 Problems · ~11 hrs

7.1 Binary Trees (DFS, BFS & Bottom-Up Recursion)

Learn what a Binary Tree is from scratch, master Preorder/Inorder/Postorder DFS and Level-Order BFS, and solve Height, Diameter, and LCA problems.

01 Read the story: What & Why
02 Follow the drawing: Visual stepper
03 Practice code: 4 languages + problems

1. What Is a Binary Tree?

Up until now, Arrays, Linked Lists, Stacks, and Queues were all 1D lines (each item had at most one next neighbor).

A Binary Tree is a hierarchical data structure (like a family tree or a folder directory on your computer):

  • It starts at a single top node called the root.
  • Every TreeNode holds a value (val) and up to two children: left and right.
  • A node with no children (left == null and right == null) is called a leaf.
  • Unlike a Graph, a Tree never has cycles — there is only one unique path from the root to any node!
Visual: Binary Tree DFS vs. Level-Order BFS Traversals
12345Preorder (Root→L→R): [1, 2, 4, 5, 3]Inorder (L→Root→R): [4, 2, 5, 1, 3]Postorder (L→R→Root): [4, 5, 2, 3, 1]BFS Level-Order: [[1], [2, 3], [4, 5]]

2. Why Do We Need Binary Trees & How Do We Walk Them?

Why Trees Exist

Flat arrays cannot represent nested hierarchies (like HTML DOM elements, organization charts, or decision trees). Trees also divide data into two branches at every level, so a balanced tree of 1,000,000 nodes is only 20 levels tall (log2(1,000,000) ≈ 20)!

The Two Ways to Traverse Every Node (O(N) Time)

  1. Depth-First Search (DFS — via Recursion): Plunge all the way down to a leaf before backing up.
    • Preorder (Root -> Left -> Right): Visit the parent before its children.
    • Inorder (Left -> Root -> Right): Visit left subtree, then parent, then right subtree.
    • Postorder (Left -> Right -> Root): Visit both children first, then combine their answers at the parent (Bottom-Up Recursion!).
  2. Breadth-First Search (BFS — via a FIFO Queue): Visit the tree floor by floor (Level 0, then Level 1, then Level 2).

Comparison Table: Which Tree Traversal Should You Pick?

Traversal OrderVisit SequenceConcrete Example on Tree [Root=4, Left=2, Right=7]When Interviewers Expect You to Use It
Preorder DFSRoot -> Left -> Right[4, 2, 7]Copying/cloning a tree, serializing a tree, or passing top-down bounds from parent to child
Inorder DFSLeft -> Root -> Right[2, 4, 7] (Sorted!)Binary Search Trees (BSTs) — visits nodes in strictly ascending sorted order
Postorder DFSLeft -> Right -> Root[2, 7, 4]Bottom-Up Tree DP (Height, Diameter, Balanced Check, Max Path Sum, Lowest Common Ancestor)
Level-Order BFSFloor by floor (Queue)[[4], [2, 7]]Right Side View, Zigzag Level Order, Minimum Depth, Level Averages
The Hand-Drawn Step-by-Step Walkthrough
Step 1 / 2

Interactive Step-by-Step Trace: DFS Postorder vs. BFS Level-Order

See how bottom-up height calculation and level-by-level queues traverse a Binary Tree:

Postorder DFS (Left → Right → Root): Compute Height Bottom-Up
h = 1Leaf (4)
h = 1Leaf (5)
1+max(1,1)=2Node (2)h = 2
1+max(2,1)=3Root (1)h = 3
return 1 + max(dfs(node.left), dfs(node.right))

A parent node cannot know its height until both children report theirs!

✎ Remember this: Use Postorder whenever a parent's answer depends on its children's answers.

3. Basic Operations & Walkthroughs (All 4 Languages)

from collections import deque

class TreeNode:
  def __init__(self, val=0, left=None, right=None):
      self.val = val
      self.left = left
      self.right = right

# 1. Bottom-Up Postorder DFS: Max Depth (Height) of a Binary Tree -> O(N)
def max_depth(root: TreeNode) -> int:
  if not root:
      return 0
  left_h = max_depth(root.left)
  right_h = max_depth(root.right)
  return 1 + max(left_h, right_h)

# 2. Level-Order BFS (Floor by Floor using a Queue) -> O(N)
def level_order(root: TreeNode) -> list[list[int]]:
  if not root:
      return []
  q = deque([root])
  levels = []
  while q:
      level = []
      for _ in range(len(q)):
          node = q.popleft()
          level.append(node.val)
          if node.left: q.append(node.left)
          if node.right: q.append(node.right)
      levels.append(level)
  return levels

4. Core Interview Patterns

  • Diameter / Max Path Sum (Postorder): At each node, update a global best using the path that curves through this node (left_h + right_h), and return the single best straight branch (1 + max(left_h, right_h)) up to the parent.
  • Lowest Common Ancestor (LCA): If root is null, p, or q, return root. Recurse left and right: if both sides return non-null, root is the exact split point where p and q meet!
Pattern: Bottom-Up Postorder DFS (Global Best vs. Single-Branch Return)

Use for Diameter of Binary Tree, Balanced Binary Tree, and Binary Tree Maximum Path Sum where a path can arch through a node, but only one branch can continue upward to its parent.

best = 0
def dfs(node: TreeNode) -> int:
  nonlocal best
  if not node: return 0
  left = max(0, dfs(node.left))
  right = max(0, dfs(node.right))
  best = max(best, node.val + left + right)  # Arch through node
  return node.val + max(left, right)         # Straight branch to parent
Operation / PatternTimeSpaceNotes
DFS Traversal (Pre/In/Postorder)O(N)O(H) stackH = tree height (O(log N) balanced, O(N) skewed)
Level-Order BFSO(N)O(W) queueW = maximum width of any level in the tree

5. Practice On Your Own (Curated Submodule Problems)

0 / 4 Solved
StatusProblemDifficultyComplexityHint?Links & Notes
Maximum Depth of Binary Tree
DFS Traversals & Bottom-Up Recursion
Easy
T: O(N)
S: O(H)
Solve
Diameter of Binary Tree
DFS Traversals & Bottom-Up Recursion
Easy
T: O(N)
S: O(H)
Solve
Binary Tree Level Order Traversal
Level-Order BFS (Queue)
Medium
T: O(N)
S: O(N)
Solve
Lowest Common Ancestor of a Binary Tree
Lowest Common Ancestor (LCA)
Medium
T: O(N)
S: O(H)
Solve