The Complete Tree Crash Course

Tree problems are among the most frequent questions in coding interviews.

Start with the vocabulary:

1) Tree Vocabulary

  • Root
  • Leaf
  • Child
  • Subtree
  • Height
  • Depth
  • Ancestor
  • Descendant

The tree you will meet most often in interviews is the binary tree.

In a binary tree every node has at most two children, called the left child and the right child.

Every node also stores a value of its own.

Traversal Methods

Follows one branch as deep as it goes before backing up to try the next.

  • Preorder
  • Inorder
  • Postorder

The three orders differ only in when you process the current node: before, between or after its subtrees.

Recursive DFS is short to write, so expect a follow up asking for the iterative version with a stack.

Visits the tree one level at a time, from the root down, using a queue.

Reach for it whenever a problem talks about levels, such as the right side view or the minimum depth.

3) Binary Search Tree (BST)

A binary search tree keeps its values in order, which makes searching fast.

Every value in a node's left subtree is smaller than the node, and every value in its right subtree is larger. Most interview versions assume there are no duplicates.

An inorder traversal of a BST visits the values in sorted order.

Know these by heart:

  • Search for a value, and handle the value being missing
  • Insert a value
  • Delete a node with zero, one or two children

4) Kinds of Binary Trees

These names come up in problem statements.

Knowing them tells you which guarantees you can rely on, for example that a complete tree fits neatly into an array.

  • Full binary tree
  • Complete binary tree
  • Perfect binary tree
  • Balanced binary tree
  • Degenerate binary tree

5) Specialized Trees

  • Heap
  • N-ary tree
  • Trie (prefix tree)
  • Segment tree

NOTES

  • Most tree solutions are recursive, so make sure you are comfortable with recursion first.
  • If a recursive solution feels obvious, practice converting it to an iterative one with an explicit stack or queue.
  • The height of a balanced tree is O(log n), but a degenerate tree can be O(n) tall, which changes the complexity.
  • Interviewers rarely ask you to implement a self balancing tree, but they do expect you to know what balanced means.

TREES ARE EVERYWHERE

File systems, the DOM, org charts and databases all store data as trees. Learn the words first.

1. Root

The topmost node, the only one with no parent.

Every traversal starts here. In this tree the root is 5.

2. Leaf

A node with no children.

1, 4 and 9 are the leaves of this tree.

3. Child

A node directly below another node.

3 and 8 are children of 5. A binary tree node has at most two.

4. Subtree

Any node together with everything below it.

The subtree rooted at 3 holds 3, 1 and 4, and is itself a tree.

5. Height

The number of edges on the longest path from a node down to a leaf.

Leaves have height 0, and the root here has height 2.

6. Depth

The number of edges from the root down to a node.

The root has depth 0, and 9 has depth 2.

7. Ancestor

Any node on the path from a node up to the root.

5 and 8 are the ancestors of 9.

8. Descendant

Any node in a node's subtree, below the node itself.

1 and 4 are the descendants of 3.

Building a Node

A binary tree node holds a value and links to a left and a right child.

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

Problems usually hand you the root node only.

From any node, node.left and node.right lead to its children, and node.val holds its value.

A missing child is None, which is the base case of most recursive tree functions.

Depth First Search

Goes deep along one branch before backing up. The three orders only differ in when the current node is processed.

  • Preorder: node, left, right
  • Inorder: left, node, right
  • Postorder: left, right, node
def inorder(node):
    if not node:
        return
    inorder(node.left)
    print(node.val)   # move this line for pre or post
    inorder(node.right)

Breadth First Search

Processes the tree one level at a time. Also called level order traversal.

from collections import deque

def level_order(root):
    queue = deque([root] if root else [])
    while queue:
        for _ in range(len(queue)):
            node = queue.popleft()
            print(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

Iterative DFS

Any recursive traversal can be rewritten with your own stack. Here is inorder:

def inorder(root):
    stack, node = [], root
    while stack or node:
        while node:            # go as far left as possible
            stack.append(node)
            node = node.left
        node = stack.pop()
        print(node.val)        # visit
        node = node.right      # then the right subtree

Preorder is even simpler: pop a node, visit it, then push its right child and its left child.

Interviewers often ask for this version once your recursive answer works.

Kinds of Binary Trees

Binary Search Tree

A binary tree that keeps its values in order.

Everything in the left subtree is smaller than the node, and everything in the right subtree is larger.

Most interview versions assume the values are unique.

Operations to know:

  • Search
  • Insert
  • Delete a node with zero, one or two children
  • Inorder traversal gives sorted order

Full Binary Tree

  • Every node has either zero or two children
  • No node has exactly one child

Complete Binary Tree

  • Every level is full except possibly the last
  • The last level fills from left to right

Perfect Binary Tree

  • Every internal node has two children
  • All leaves sit on the same level

Balanced Binary Tree

  • For every node, the heights of its two subtrees differ by at most one

Degenerate Binary Tree

  • Every parent has exactly one child
  • It behaves like a linked list, so its height is O(n)

Specialized Trees

Heaps

A complete binary tree with one ordering rule: in a min heap every parent is smaller than or equal to its children.

It gives you the smallest element in O(1) and removes it in O(log n), which is why heaps power priority queues.

Because the tree is complete, it is stored in a plain array: the children of index i live at 2i + 1 and 2i + 2.

N-ary Trees

Nodes can have any number of children, stored as a list. File systems and comment threads look like this.

Traversals work the same way, except you loop over every child instead of visiting left and right.

def dfs(node):
    for child in node.children:
        dfs(child)

Trie (Prefix Tree)

Each node stands for one character, and the path from the root spells a prefix.

Looking up a word or a prefix takes time proportional to its length, no matter how many words are stored.

Green nodes end a word: cat, car and dog.

Tries are the go-to structure for autocomplete and word search problems.

Segment Trees

Each node stores a summary, such as the sum, of a range of an array. The root covers the whole array and each child covers half of its parent.

Range queries and single updates both take O(log n).

Rare in interviews, but worth recognizing when a problem mixes range queries with updates.