Skip to content

Intermediate: core algorithms · Trees and binary search trees · topic 31 of 61 · 30 min

Trees: words and shapes

Explanation

A tree is a set of nodes joined by edges, with one node at the top (the root) and no loops. Each node can have children, and nodes without children are leaves. In a binary tree every node has at most two children, called left and right. You store a binary tree either as linked nodes (a TreeNode class) or, when it is complete, in a plain array where the children of index i sit at 2i + 1 and 2i + 2.

Why trees?

Arrays, stacks and linked lists put items in a line. Many things in life are not a line but a hierarchy: folders inside folders, a company org chart, an HTML page where tags sit inside tags (the DOM), a family tree, or product categories such as Electronics → Mobiles → Smartphones. A tree is the data structure for hierarchies. It also gives fast searching when it is kept in order (binary search trees, Topic 4) and fast "give me the smallest" when it is kept as a heap (Module 8).

The words

WordMeaning
NodeOne item in the tree. It holds a value and links to its children.
EdgeThe link from a parent to a child. A tree with n nodes has exactly n − 1 edges.
RootThe single node at the top. It has no parent.
Parent / childIf an edge goes from A down to B, A is the parent of B and B is a child of A.
LeafA node with no children.
SubtreeA node together with everything below it. Every node is the root of its own subtree.
Depth of a nodeThe number of edges from the root down to it. The root has depth 0.
Height of a nodeThe number of edges on the longest path from it down to a leaf. A leaf has height 0.
Height of a treeThe height of its root.

A tree has no cycles: from the root there is exactly one path to every node. That single-path rule is what separates a tree from a general graph (Module 9).

Careful: Interview problems often count NODES instead of edges. LeetCode's "maximum depth of a binary tree" is the number of nodes on the longest root-to-leaf path, so a single node has maximum depth 1 there, but height 0 in the edge counting above. Always say which one you mean.

Binary trees and their shapes

In a binary tree each node has at most two children, a left child and a right child. Left and right matter: a root with only a left child is a different tree from a root with only a right child.

  • Full: every node has 0 or 2 children, never exactly 1.
  • Complete: every level is completely filled except possibly the last, and the last level is filled from the left with no gaps. Heaps are complete trees.
  • Perfect: every internal node has 2 children and all leaves are on the same level. A perfect tree of height h has 2^(h + 1) − 1 nodes, for example 7 nodes for height 2.
  • Balanced (height-balanced): at every node, the heights of the left and right subtrees differ by at most 1. Its height is O(log n).
  • Skewed (degenerate): every node has only one child, so the tree is really a linked list and its height is n − 1.

The shape decides the speed. Most tree operations walk from the root down one path, so they cost O(h), where h is the height. In a balanced tree h is about log₂ n (around 20 for a million nodes); in a skewed tree h is n − 1. That difference is the whole reason balanced trees exist.

Representation 1: linked nodes

The usual way is a small class with a value and two links. None means "no child". In this module every task gives you the same TreeNode class and a build(values) helper. build takes a level-order list, the way LeetCode shows trees: the root first, then its children left to right, then the next level, with None for a missing child.

output
        1
       / \
      2   3
     / \   \
    4   5   6
The tree that build([1, 2, 3, 4, 5, None, 6]) creates. The None says node 3 has no left child.
python
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

root = build([1, 2, 3, 4, 5, None, 6])
print(root.val, root.left.val, root.right.val)    # Output: 1 2 3
print(root.left.left.val, root.right.right.val)   # Output: 4 6
print(root.right.left)                            # Output: None
build is given in every task; you only write the function the task asks for.

Representation 2: an array (heap layout)

A complete binary tree can live in a plain list with no links at all. Put the root at index 0 and fill each level left to right. Then for the node at index i: the left child is at 2i + 1, the right child at 2i + 2, and the parent at (i − 1) // 2. No pointers are stored, which saves memory and keeps the data side by side. Heaps (Module 8) use exactly this layout.

python
arr = [1, 2, 3, 4, 5, None, 6]
i = 1                     # the node with value 2
print(arr[2 * i + 1], arr[2 * i + 2], arr[(i - 1) // 2])   # Output: 4 5 1
Children and parent by arithmetic. For a sparse or skewed tree this wastes space on gaps, so linked nodes are better there.

Your first recursive tree function

Almost every tree function follows one template: handle the empty tree (None), then combine the answers of the left and right subtrees. Counting nodes: an empty tree has 0 nodes; otherwise it is 1 (this node) plus the count on the left plus the count on the right. It visits each node once, so it is O(n) time, and the recursion goes as deep as the tree, so it uses O(h) stack space.

python
def count_nodes(root):
    if root is None:
        return 0
    return 1 + count_nodes(root.left) + count_nodes(root.right)

print(count_nodes(build([1, 2, 3, 4, 5, None, 6])))   # Output: 6

Common mistakes

  • Forgetting the empty tree. root.left on None raises AttributeError. Fix: start every function with if root is None.
  • Mixing up depth and height, or edges and nodes. Fix: say "height in edges" or "depth in nodes" out loud, and test a single node.
  • Treating a leaf as "a node with at most one child". A leaf has NO children: node.left is None and node.right is None.
  • Assuming every binary tree is a BST or balanced. Fix: only use ordering or O(log n) when the problem promises it.
  • Using the array layout for a sparse tree. A skewed tree of 30 nodes would need about a billion slots. Fix: use linked nodes unless the tree is complete.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Trees: words and shapes · 1/9

Trees

This lesson introduces trees: the words people use for them and the shapes you will meet in interviews.

Prefer a video by a teacher? Find free videos on “binary tree terminology root leaf height depth complete full perfect explained” on YouTube ↗

In real life: An org chart is a tree

A company's HR system stores who reports to whom. The CEO is the root, each manager is a parent of the people who report to them, and people with no reports are leaves. "How many levels of management are there?" is the height of the tree, and "how many people are in the technology team?" is the size of one subtree. Folder sizes on your laptop and nested categories on a shopping site are worked out the same way.

python
org = {
    "CEO": ["CTO", "CFO"],
    "CTO": ["Dev lead", "QA lead"],
    "CFO": [],
    "Dev lead": ["Intern"],
    "QA lead": [],
    "Intern": [],
}

def levels(person):
    return 1 + max((levels(r) for r in org[person]), default=0)

print(levels("CEO"))
Shows 4: CEO, CTO, Dev lead, Intern is the longest chain of reports.

Practice (0/4 done)

Task 1 · Code · Python

Write count_nodes(root) that returns the number of nodes in the binary tree. root is a TreeNode or None (an empty tree has 0 nodes). TreeNode and build(values) are already defined above your function. Example: count_nodes(build([1, 2, 3, 4, 5, None, 6])) returns 6.

Task 2 · Code · Python

Write max_depth(root) that returns the number of NODES on the longest path from the root down to a leaf. An empty tree returns 0 and a single node returns 1. Example: max_depth(build([3, 9, 20, None, None, 15, 7])) returns 3.

Task 3 · Code · Python

Write count_leaves(root) that returns how many leaves the tree has. A leaf is a node with NO children (both left and right are None). An empty tree has 0 leaves; a single node is 1 leaf. Example: count_leaves(build([1, 2, 3, 4, 5, None, 6])) returns 3.

Task 4 · Code · Python

A COMPLETE binary tree is stored in a list arr in heap layout (root at index 0, children of index i at 2i + 1 and 2i + 2, no gaps). Write array_leaves(arr) that returns the values of all leaves in index order. Use the layout rule: index i is a leaf when its left child index 2i + 1 is outside the list. Example: array_leaves([1, 2, 3, 4, 5]) returns [3, 4, 5].

Quick quiz

Question 1

1.A tree has 12 nodes. How many edges does it have?

Question 2

2.Which statement about a complete binary tree is true?

Question 3

3.In the array layout, the node at index 4 has its right child at which index?

Question 4

4.Why is searching down one path O(log n) in a balanced tree but O(n) in a skewed one?

Question 5

5.A tree is a single node. What is its height in edges, and its maximum depth counted in nodes?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A tree has 12 nodes. How many edges does it have?

Answer: 11

Every node except the root has exactly one edge to its parent, so n − 1 = 11.

Which statement about a complete binary tree is true?

Answer: Every level is full except maybe the last, which fills from the left

That is the definition of complete. The first is "full", the second "perfect", and the last is a BST rule.

In the array layout, the node at index 4 has its right child at which index?

Answer: 10

The right child is at 2i + 2 = 10; the left child is at 2i + 1 = 9.

Why is searching down one path O(log n) in a balanced tree but O(n) in a skewed one?

Answer: The cost is O(height), and the height is about log n when balanced but n − 1 when skewed

Walking down one path costs one step per level, and the number of levels is the height.

A tree is a single node. What is its height in edges, and its maximum depth counted in nodes?

Answer: 0 and 1

There are no edges, so height 0; the longest path has one node, so maximum depth 1.

What is the extra space of a recursive tree function on a balanced tree of n nodes?Related

Answer: O(log n)

The recursion stack holds one path from the root, and a balanced tree has height O(log n).

From Recursive tree patterns

Which traversal frees a tree safely?Related

Answer: Post-order

Children must be freed before the parent.

From Binary search trees

Deleting a node with two children: which value usually replaces it?Related

Answer: The smallest value in its right subtree (the inorder successor)

The successor is larger than everything on the left and smaller than everything else on the right, so the BST rule still holds.

From Binary search trees

Pop on a binary heap moves the last item to the root and then…Related

Answer: sifts it down, swapping with the smaller child while it is bigger

The moved item may be too big for the root, so it swaps downward with its smaller child until the heap property holds.

From Heaps and priority queues

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Trees and binary search trees” before its exam: Traversals: DFS and BFS, Recursive tree patterns, Binary search trees, Lowest common ancestor, serialisation and balanced trees.
  2. Module 7 exam: Trees and binary search trees · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Heaps and greedy algorithms. 61 topics left in the course before the final exam.

← Previous: Binary search on the answer