When to use a tree
Use a tree when your data is a hierarchy:
- One parent, many children: folders and files, an organisation chart, a menu, an HTML page.
- A nested document such as JSON or XML.
- Splitting a problem into smaller versions of itself, for example the choices in a game or a decision.
- Ordered data that changes (a binary search tree), or priorities (a heap, which is a tree stored in an array).
Use something else when:
- Items link to many others in any direction, or form loops: use a graph.
- You need position access to a plain sequence: use an array.
- You only look up exact keys: use a hash map.
- You need fast sorted operations and the tree might become lopsided: use a balanced tree, not a plain binary tree.
Do not build general trees from scratch when a library has one. Many languages have sorted maps built on balanced trees, and the browser gives you the page as a tree. Writing a node class is for learning and interviews.
How a tree works
A tree is a set of nodes joined by edges with a root at the top.
Every node except the root has exactly one parent, and there are no cycles, so there is exactly one path from the root to any node.
Words you will meet:
- Root: the top node. Leaf: a node with no children.
- Parent and child: a node and the nodes directly below it.
- Subtree: a node together with everything below it. Every subtree is a smaller tree.
- Depth: how far a node is from the root. Height: the longest path from the root down to a leaf.
A binary tree is a tree where each node has at most two children, called left and right. A binary search tree is a binary tree with an ordering rule (see the Binary Search Tree lesson). A plain binary tree has no ordering rule.
Trees come in shapes. A balanced tree keeps its height close to log n. A lopsided (skewed) tree is more like a linked list, with height close to n. The height matters because most operations cost O(h).
Walking a tree: the four traversals
To traverse a tree is to visit every node once. There are four standard orders:
| Order | Visits | Typical use |
|---|---|---|
| Pre-order | node, left, right | Copying a tree, printing a structure from the top |
| In-order | left, node, right | Reading a binary search tree in sorted order |
| Post-order | left, right, node | When a node needs its children's answers first: folder sizes, deleting a tree |
| Level order | top to bottom, left to right | Shortest steps, "by level" questions |
The first three go deep first and use recursion (the call stack remembers the way back). Level order goes wide first and uses a queue.
Try it
Choose a traversal order and a tree, then step through. Watch the path from the root (or the queue) and the output so far.
Tree traversal
Step 1 of 27 · Press Space to play, arrow keys to stepRule: left, node, right
Path from the root: 1
Output so far: nothing yet
Go to the root to 1.
- visited:
- 0
- depth:
- 1
- height:
- 3
depth = how many nodes are on the path from the root to where we are. queue = nodes waiting in level order.
Things to try:
- In-order on the Full tree: the output is
4, 2, 5, 1, 6, 3, 7. Then Pre-order:1, 2, 4, 5, 3, 6, 7. Same tree, same nodes, different order, because the node is visited at a different moment. - Post-order on the full tree gives
4, 5, 2, 6, 7, 3, 1. The root comes last, because it waits for both children. - Pick the Lopsided tree and run In-order. The path grows to 4 nodes deep, as deep as the tree is tall. On a lopsided tree the recursion goes as deep as the number of nodes.
- Level order uses a queue, not a path. Watch the queue hold one whole level at a time.
Tree operations: build, traverse and combine
Build. Each node stores a value and links to its left and right child (or none). A tree is just a link to its root.
Traverse. Write one function that handles one node and then calls itself on the left and right children. The only choice is when to visit the node relative to those two calls. A missing child (empty) is the base case that ends the recursion.
Combine. Most problems follow one shape: solve the left side, solve the right side, then combine the two answers at the current node. For example:
- Height of a tree = 1 + the larger of the heights of the two children.
- Number of nodes = 1 + nodes on the left + nodes on the right.
- Total size of a folder = its own size + the sizes of its children.
Level order. Put the root in a queue. Repeatedly take the front node, visit it, and add its children to the back.
Interview fact. Every traversal visits each node once, so it takes O(n) time. The extra memory is O(h) for the recursive orders, where h is the height, and O(w) for level order, where w is the widest level.
Tree time complexity
| Operation | Normal case | Worst case |
|---|---|---|
| Visit every node (any traversal) | O(n) | O(n) |
| Find a value in a tree with no ordering | O(n) | O(n) |
| Height, size, sum | O(n) | O(n) |
| Memory for pre, in or post-order | O(h) | O(n) for a lopsided tree |
| Memory for level order | O(w) | O(n) |
| Memory to store the tree | O(n) | O(n) |
The height h is about log n for a balanced tree and n for a lopsided one, which is why balance matters.
Trees vs other data structures
The right choice depends on what you do most. n is the number of items.
| What you need | Binary tree | Balanced search tree | Array or list | Graph |
|---|---|---|---|---|
| Model a hierarchy | Natural fit | Only if it is ordered | Awkward | Possible, but allows loops |
| Visit every item | O(n) | O(n) | O(n) | O(V + E) |
| Find a value (no ordering rule) | O(n) | Not applicable | O(n) | O(V + E) |
| Find a value (ordered keys) | O(n) | O(log n) | O(n), or O(log n) if sorted | O(V + E) |
| Items in sorted order | Only if built as a search tree | O(n) by in-order walk | O(n) if sorted | Not natural |
| Loops allowed | No | No | Not applicable | Yes |
The bold cells show where each wins. A tree is the most natural fit for hierarchies, and a balanced search tree is the one to pick when you need fast ordered operations.
How to explain trees in an interview
- Say the definition: "nodes with one parent each and no cycles, so one path from the root to any node."
- Define height and say the cost in terms of it: "operations are O(h), which is log n if balanced and n if lopsided."
- Name the traversal you will use and why: "post-order, because each node needs its children's answers first."
- Write one recursive function and state what it returns, then say the base case for an empty node.
- Say the memory: "O(h) for the recursion stack."