InterviewKata
Data structure 9 of 12

Trees and binary trees

A tree is a hierarchy: one node at the top, each node pointing down to its children. Every node with everything below it is a smaller tree, which is why tree code is almost always recursive.

A 3-minute conversation: Sam asks, Priya explains
0:00 / 2:33

1. What it is

The folders on your computer. One top folder; inside it, more folders; inside those, more again, until you reach files with nothing inside. To count every file, you count the files in each sub-folder the same way and add them up.

A tree is made of nodes joined by edges. The vocabulary you will see in every problem:

2. How it sits in memory

Each node is a small object holding a value and references to its left and right children (None when missing). The whole tree is reached through one reference: the root.

LeetCode writes trees as a list read level by level, left to right, with null for a missing child: [3, 9, 20, null, null, 15, 7]. The page's first operation turns that list into real nodes.

3. Operations and what they cost

TaskCostWhy
Visit every nodeO(n)Each node once.
Height, count, sum, find a valueO(n)A plain binary tree has no order to exploit (a search tree does: structure 10).
Recursion depthO(h) stack framesh is the height: about log n when balanced, up to n when the tree is a long chain.
Reach a childO(1)Follow left or right.
Reach the parentnot possible directlyNodes don't point up; carry the parent in your recursion if you need it.
The recursive recipe1. If the node is empty, return the answer for an empty tree. 2. Ask both children for their answers. 3. Combine them with this node's value. Almost every tree problem fits.

4. In Python, TypeScript and Java

No language has a built-in binary tree for interviews; problems give you a node class like this one.

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

    #      3
    #     / \
    #    9   20
    #       /  \
    #      15   7
    root = TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))
    print(root.right.left.val)    # 15

5. Core operations, step by step

Build a tree from LeetCode's list, then the two shapes of recursion you'll write constantly: visiting every node, and combining answers from the children.

A · Build a tree from a level-order list

What it doesCreates the root, then hands out children to nodes in the order they were created, using a queue.
Picture itFilling seats row by row in a theatre: each person in the queue gets the next two seats behind them, or an empty seat where the list says null.
CostO(n).

The code. You rarely write this in an interview, but knowing it makes LeetCode's tree lists readable.

def build(vals):
    root = TreeNode(vals[0])
    q = deque([root])
    i = 1
    while i < len(vals):
        node = q.popleft()
        if vals[i] is not None:
            node.left = TreeNode(vals[i])
            q.append(node.left)
        i += 1
        if i < len(vals) and vals[i] is not None:
            node.right = TreeNode(vals[i])
            q.append(node.right)
        i += 1
    return root

Line by line.

LineWhat it doesWhy
node = q.popleft()The next node waiting for children.Nodes receive children in the order they were created: level by level.
if vals[i] is not NoneA real left child?null means this node has no left child; nothing joins the queue.
i += 1Move along the list after each child slot.Both slots consume a list entry, even null ones.

Watch it run. [3, 9, 20, null, null, 15, 7].

Watch out for:

  • Treating the list as heap positions. LeetCode lists skip the children of missing nodes, so index 2i + 1 isn't always the left child.

B · Visit every node (count them)

What it doesCounts the node itself plus the counts of both subtrees.
Picture itCounting files: each folder reports 1 for itself plus whatever its sub-folders report.
CostO(n) time, O(h) stack.

The code. The recursive recipe in its smallest form.

def count(node):
    if node is None:
        return 0
    left = count(node.left)
    right = count(node.right)
    return 1 + left + right

Line by line.

LineWhat it doesWhy
if node is None: return 0An empty tree has no nodes.This base case is where every path of recursion stops.
left = count(node.left)Ask the left subtree.Trust the recursion: it returns the right answer for the smaller tree.
return 1 + left + rightMe plus both subtrees.

Watch it run. Count the nodes of [3, 9, 20, null, null, 15, 7].

Watch out for:

  • Forgetting the None case. node.left on None crashes. Check for an empty node first.

C · Combine answers from the children (height)

What it doesHeight = 1 + the taller of the two subtrees' heights.
Picture itHow deep is the folder tree? One for this folder, plus the deepest sub-folder chain.
CostO(n) time, O(h) stack.

The code. Same recipe; only the combining line changes.

def height(node):
    if node is None:
        return 0
    left = height(node.left)
    right = height(node.right)
    return 1 + max(left, right)

Line by line.

LineWhat it doesWhy
return 1 + max(left, right)One for me, plus the deeper side.Use max, not +: height follows one path, not both.

Watch it run. The height of [3, 9, 20, null, null, 15, 7].

Watch out for:

  • Adding left and right. That counts nodes on both sides, not the longest path.
  • Returning −1 or 0 for a leaf by mistake. Decide what an empty tree returns (0 here), and a leaf follows: 1 + max(0, 0) = 1.

6. How to remember it

Memory hook: "Folders inside folders"Every folder is a smaller copy of the whole. Ask each sub-folder for its answer, combine with your own, and the empty folder is where you stop.

Picture

A file explorer with folders expanded, each folder reporting a number up to its parent.

Chant

"Empty? Base case. Ask left, ask right, combine."

Key rule

Height takes the max of the children; counts and sums add them.

7. Problems solved with it

Two easy problems: one asks the children a yes/no question, the other passes information down.

LC 965 · Univalued Binary Tree

The problemReturn true if every node in the tree has the same value.
What it doesEach subtree must be univalued with the root's value.
CostO(n).

The code. Pass the value to match down; combine the children's answers with and.

def is_unival(node, value):
    if node is None:
        return True
    if node.val != value:
        return False
    return is_unival(node.left, value) and is_unival(node.right, value)

Line by line.

LineWhat it doesWhy
if node is None: return TrueAn empty subtree breaks nothing.
if node.val != value: return FalseA different value: the answer is no.Stop early; no need to look further.
... and ...Both subtrees must agree.and stops at the first false.

Watch it run. [2, 2, 2, 5, 2].

Watch out for:

  • Only comparing each node with its parent. That works too, but passing the root's value down is simpler.

LC 404 · Sum of Left Leaves

The problemReturn the sum of all leaves that are the left child of their parent.
What it doesPasses "am I a left child?" down; adds a leaf's value only when it is.
CostO(n).

The code. Information travels down as an argument and answers travel up as return values.

def sum_left_leaves(node, is_left=False):
    if node is None:
        return 0
    if node.left is None and node.right is None:
        return node.val if is_left else 0
    return sum_left_leaves(node.left, True) + sum_left_leaves(node.right, False)

Line by line.

LineWhat it doesWhy
if node.left is None and node.right is NoneA leaf?A leaf has no children at all.
return node.val if is_left else 0Count it only if it hangs on the left.The parent told us which side we are.
sum_left_leaves(node.left, True)Tell the left child it's on the left.

Watch it run. [3, 9, 20, null, null, 15, 7]: the left leaves are 9 and 15.

Watch out for:

  • Counting the root when the tree is one node. A lone root isn't a left child: the answer is 0.

More practice, solved on the pattern pages

#ProblemLevelWhere
104Maximum Depth of Binary TreeEasySolved step by step on 10 · Binary Tree Traversal
226Invert Binary TreeEasySolved step by step on 10 · Binary Tree Traversal
100Same TreeEasySolved step by step on 10 · Binary Tree Traversal
101Symmetric TreeEasySolved step by step on 10 · Binary Tree Traversal
543Diameter of Binary TreeEasySolved step by step on 10 · Binary Tree Traversal
112Path SumEasySolved step by step on 11 · Depth-First Search (DFS) on Graphs

8. Common mistakes

No base case for an empty nodeEvery recursive tree function must handle None first. Without it, node.left crashes on a missing child.
Mixing up height and depthDepth counts down from the root; height counts up from the leaves. Read which one the problem wants, and whether it counts nodes or edges.
Recomputing the same subtreeCalling height(node.left) twice per node can make an O(n) solution O(n²). Store results in variables.
Deep, unbalanced treesA tree shaped like a chain has height n; recursion that deep can overflow. An explicit stack avoids it.
Reading LeetCode lists as heap positionsChildren of a missing node are left out of the list. Build the tree properly (operation A).

9. Self-check quiz

What is a leaf?

Leaves are where every root-to-leaf path ends.

Given the heights of both subtrees, the height of the tree is…

One for this node, plus the deeper side.

How much stack does recursion on a tree use?

One frame per level on the current path: log n if balanced, n if a chain.

In [3, 9, 20, null, null, 15, 7], what is 20's left child?

9's children are the two nulls; then 20 receives 15 (left) and 7 (right).

10. Where it shows up next

Next structure: 10 · Binary search trees, trees with an ordering rule that makes searching fast.