Trees: terminology, binary trees and traversals
Terms such as root, leaf, depth and height, general and binary trees, preorder/inorder/postorder/level-order traces, and expression trees.
Lists, stacks and queues (SEN2211) were linear structures: each element had at most one “next”. A tree is a hierarchical structure: an element can have many “children”. File systems, HTML documents, a compiler’s expression trees and database indexes are all trees. The next six weeks of this course (BST, AVL, red-black, heap) build on the terms on this page.
The questions from this week in the exam are: terminology (depth, height, number of leaves in a given tree), traversal traces (preorder, inorder, postorder, level-order), expression trees (build from postfix, evaluate) and building a tree from two traversals.
Terminology
Let’s take this general tree as our example:
A
/ | \
B C D
/ \ / \
E F G H
|
I
| Term | Meaning | In the example |
|---|---|---|
| root | the only node with no parent | A |
| parent / child | directly connected upper / lower node | G’s parent is D, its child is I |
| sibling | children of the same parent | B, C, D |
| leaf | a node with no children | E, F, C, I, H |
| internal node | a node with at least one child | A, B, D, G |
| degree | number of children | A: 3, G: 1 |
| ancestor / descendant | nodes on the path from the root to a node / nodes in its subtree | I’s ancestors are G, D, A |
| depth | number of edges from the root to the node | I: 3 |
| height | number of edges from the node to the deepest leaf | D: 2, tree: 3 |
| subtree | a node and all its descendants | D, G, H, I |
Two basic facts: a tree with n nodes has n - 1 edges (every node except the root has exactly one parent edge). There is exactly one path from the root to any node.
PITFALL · depth and height are measured in opposite directions. Depth goes top to bottom (the root has depth 0), height goes bottom to top (a leaf has height 0). Some sources count nodes, not edges. In this course and in Weiss we count edges; the height of an empty tree is -1.
Storing a general tree: first-child, next-sibling
If you do not know how many children a node will have, you cannot put a fixed number of references in each node. The solution: keep two references in each node, the first child and the next sibling on the right.
class TreeNode {
String element;
TreeNode firstChild; // leftmost child
TreeNode nextSibling; // sibling to the right
}
In the tree above, A.firstChild = B, B.nextSibling = C, C.nextSibling = D, D.firstChild = G. This representation actually turns any general tree into a binary tree.
For a general tree, preorder (the node first, then its children from left to right) and postorder (the children first, then the node) are defined:
preorder : A B E F C D G I H
postorder: E F B C I G H D A
File system example: printing a directory listing with indentation is preorder; computing the total size of a directory is postorder (you need the sizes of the subdirectories first).
Binary trees
In a binary tree every node has at most two children, and the children are distinguished as left and right. A node that has only a right child is a different tree from one that has only a left child.
class Node {
int element;
Node left, right;
Node(int e) { element = e; }
}
Special kinds of binary trees
| Kind | Definition |
|---|---|
| full (proper) | every node has 0 or 2 children |
| perfect | full and all leaves at the same depth |
| complete | every level is full except the last, and the last level is filled from left to right |
The complete tree will be the shape of a heap in week 7. The important numbers:
In the worst case (every node has a single child) the height is and the tree degenerates into a linked list. Since the running time of tree operations depends on the height, keeping the tree short is the main theme of the coming weeks.
Traversals
Let’s trace the four traversals on this binary tree:
A
/ \
B C
/ \ \
D E F
/ / \
G H I
| Traversal | Rule | Result |
|---|---|---|
| preorder | root, left, right | A B D E G C F H I |
| inorder | left, root, right | D B G E A C H F I |
| postorder | left, right, root | D G E B H I F C A |
| level-order | level by level, left to right | A B C D E F G H I |
The code is three lines; only the position of the “visit” line changes:
static void preorder(Node n) {
if (n == null) return;
visit(n); // first
preorder(n.left);
preorder(n.right);
}
static void inorder(Node n) {
if (n == null) return;
inorder(n.left);
visit(n); // in the middle
inorder(n.right);
}
static void postorder(Node n) {
if (n == null) return;
postorder(n.left);
postorder(n.right);
visit(n); // last
}
Level-order is not recursive; it uses a queue: put the root in the queue, and at each step remove from the front, visit, and add its children (left first) to the back.
static void levelOrder(Node root) {
Queue<Node> q = new ArrayDeque<>();
if (root != null) q.add(root);
while (!q.isEmpty()) {
Node n = q.poll();
visit(n);
if (n.left != null) q.add(n.left);
if (n.right != null) q.add(n.right);
}
}
All four visit each node once: time. The recursive ones use stack memory; level-order uses queue memory as large as the widest level.
TIP · the hand-traversal trick. Imagine a line that starts on the left and walks counterclockwise around the tree. If you write a node when the line touches its left side, you get preorder; when it touches the bottom, inorder; when it touches the right side, postorder.
Expression trees
In an expression tree the leaves are operands (numbers or variables) and the internal nodes are operators. (3 + 4) * (5 - 2):
*
/ \
+ -
/ \ / \
3 4 5 2
- postorder → postfix:
3 4 + 5 2 - * - preorder → prefix:
* + 3 4 - 5 2 - inorder + parentheses → infix:
((3 + 4) * (5 - 2)) - value: evaluate in postorder: first the two subtrees, then the operator → 7 * 3 = 21
Building a tree from postfix
It is the same as the postfix evaluation algorithm from SEN2211, but you push nodes instead of numbers:
- If an operand arrives, make a single-node tree and push it.
- If an operator arrives, pop the right subtree first, then the left, make an operator node, and push it.
- At the end exactly one tree is left on the stack.
The stack state for 6 2 / 1 + 4 *:
6 -> [6]
2 -> [6, 2]
/ -> [(6/2)]
1 -> [(6/2), 1]
+ -> [((6/2)+1)]
4 -> [((6/2)+1), 4]
* -> [(((6/2)+1)*4)] value 16
Building a tree from two traversals
Preorder (or postorder) tells the root, and inorder separates left from right. The algorithm:
- The first element of the preorder is the root.
- Find the root in the inorder. What is to its left is the left subtree, what is to its right is the right subtree.
- In the preorder, the next elements after the root, as many as the left subtree has, are the left subtree’s preorder; the rest is the right’s.
- Repeat for each subtree.
Example: preorder A B D E C F, inorder D B E A F C.
root A inorder: [D B E] A [F C]
left: pre B D E, in D B E -> root B, left D, right E
right: pre C F, in F C -> root C, left F
A
/ \
B C
/ \ /
D E F
postorder: D E B F C A
The same is done with postorder + inorder; this time the root is the last element of the postorder.
Common mistakes
| Wrong idea | The truth |
|---|---|
| The height of a leaf is 1 | Edges are counted: 0. An empty tree is -1 |
| The depth of the root is 1 | 0 |
| Level-order is written with recursion | It is written with a queue; recursion gives DFS order |
| When building a tree from postfix, the first popped is the left child | The first popped is the RIGHT child (8 2 - → 8 - 2) |
| Inorder always gives the correct infix of an expression tree | Without parentheses, precedence is lost |
| Preorder + postorder determines the tree | Not with single-child nodes; inorder is required |
| A node with only a right child is the same as one with only a left child | They are different trees in a binary tree |
| n edges in a tree with n nodes | n - 1 edges |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
For the general tree below, give the depth of T, the height of the tree, the degree of N and the number of leaves.
K
/ | \
L M N
/ \ |
O P Q
/ | \
S T UShow answer
Depth is the number of edges from the root to the node: K → N → Q → T, 3 edges.
The height of the tree is the depth of the deepest leaf: S, T, U are at depth 3, so the height is 3.
Degree is the number of children: N has a single child Q, so the degree is 1.
Leaves (no children): O, P, M, S, T, U → 6. Internal nodes: K, L, N, Q → 4.
Give the preorder and inorder traversals of the binary tree below.
1
/ \
2 3
/ / \
4 5 6
\ /
7 8Show answer
Preorder = root, left, right. 1 → left subtree (2, 4, 7) → right subtree (3, 5, 6, 8).
2 has no right child; 4 has no left child and its right child is 7. So 1 2 4 7, then 3 5 6 8.
Inorder = left, root, right. Go to the far left: 4 has no left child, so write 4, then its right child 7. Then 2, then the root 1.
On the right side: 5, 3, then 6's left child 8, then 6: 4 7 2 1 5 3 8 6.
For the same tree as the previous question, give the postorder and level-order traversals.
1
/ \
2 3
/ / \
4 5 6
\ /
7 8Show answer
Postorder = left, right, root. The root is always last. The subtree of 4: 7 4. Then 2. The right side: 5, then the subtree of 6 is 8 6, then 3. Finally 1.
Level-order uses a queue, level by level from left to right: level 0: 1; level 1: 2 3; level 2: 4 5 6; level 3: 7 8.
Build the expression tree for the postfix expression 8 2 3 * - 4 2 / +. What is its prefix (preorder) form and its value?
Show answer
Use a stack: push a number as a single node; when an operator arrives, pop two nodes (right first, then left), make an operator node, and push it.
8, 2, 3 are pushed. *: (2*3). -: (8 - (2*3)). 4, 2 are pushed. /: (4/2). +: the root.
The tree: root +, left child -(8, *(2,3)), right child /(4,2). Preorder: + - 8 * 2 3 / 4 2.
Value: 8 - 6 = 2, 4 / 2 = 2, 2 + 2 = 4.
A binary tree has preorder P Q S T R U V and inorder S Q T P U R V. What is its postorder traversal?
Show answer
The first element of the preorder is the root: P. In the inorder, what is left of P (S Q T) is the left subtree, and what is right (U R V) is the right subtree.
The preorder of the left subtree is Q S T: root Q, inorder S Q T → left child S, right child T.
The preorder of the right subtree is R U V: root R, inorder U R V → left child U, right child V.
Postorder: left subtree (S T Q), right subtree (U V R), root P → S T Q U V R P.
What is the maximum number of nodes in a binary tree of height 4? What is the minimum possible height of a binary tree with 100 nodes?
Show answer
Depth d has at most nodes. For height h the total is . h = 4 → 31.
The height of a binary tree with n nodes is at least . , so 6.
Check: with height 5, at most 63 nodes would fit, which is not enough for 100.
How many null child references (null links) does the 8-node binary tree in question 2 contain? State the general rule.
Show answer
Each node has 2 references: 2n in total. Every node except the root is pointed to by exactly one reference: n - 1 non-null references.
Null ones: 2n - (n - 1) = n + 1. For n = 8, that is 9.
Counting by hand: the left of 4, both children of 7, the right of 2, both children of 5, both children of 8, the right of 6 → 9.
A full binary tree (every node has 0 or 2 children) has 12 leaves. How many internal nodes and how many nodes in total does it have?
Show answer
In a full binary tree, the number of leaves L and the number of internal nodes I satisfy L = I + 1.
Why: start with a single node (L=1, I=0). Giving a leaf two children increases I by 1 and L by 1 (one leaf is gone, two arrive). The difference always stays 1.
L = 12 → I = 11, total 23.
Which traversal of an expression tree produces the postfix expression? Which one produces the fully parenthesized infix expression?
Show answer
In postfix the operator comes after its two operands: left, right, root = postorder.
In prefix it comes first: preorder.
In infix it is in the middle: inorder. But plain inorder loses precedence, so you need to put each internal node's subtree in parentheses: ((3 + 4) * (5 - 2)).
Write a recursive Java method int countLeaves(Node n) that returns the number of leaves in a binary tree (Node has fields left and right).
Show answer
Three cases: an empty tree has 0 leaves, a node with no children is 1 leaf, otherwise the sum of the two subtrees.
static int countLeaves(Node n) {
if (n == null) return 0;
if (n.left == null && n.right == null) return 1;
return countLeaves(n.left) + countLeaves(n.right);
}Each node is visited once: O(n). For the tree in question 2 the result is 3 (7, 5, 8).Write a recursive Java method int height(Node n) for a binary tree, using the convention height(null) = -1 so that a single node has height 0.
Show answer
The height of a node is the height of the taller subtree plus 1. With an empty tree at -1, a leaf gives 1 + max(-1, -1) = 0.
static int height(Node n) {
if (n == null) return -1;
return 1 + Math.max(height(n.left), height(n.right));
}For the tree in question 2 the result is 3 (1 → 2 → 4 → 7). O(n).Can the preorder and postorder traversals together always determine a binary tree uniquely? Justify with an example.
Show answer
Think of two trees: (1) A with only a left child B, (2) A with only a right child B.
Both have preorder A B and postorder B A. But the inorder differs: (1) B A, (2) A B.
Preorder or postorder gives the root, and inorder separates left from right. If a node with a single child exists, you need the inorder to build the tree.
Verification: All traversal orders, depth/height/degree values, expression tree results, the postorder of trees built from preorder+inorder, the null link counts and the L = I + 1 property were computed with Java (verify/SEN2212/W02.java, java W02.java).