ITC 120 › Midterm › Trees and BSTs
Trees and BSTs · Lesson 5 of 6
Recursion and Traversal Code
This lesson teaches:TechniqueA method that calls itself, and how it walks a tree.
By the end, you can:
- point out the base case and the recursive case in a recursive method;
- draw the call stack and say where Java continues after a call finishes;
- trace
preOrder,inOrder, andpostOrderin Java and write their output; - trace the recursive insert, read the same code written with other names, and write a recursive
heightmethod.
You need: the insertion and traversals lessons, and scratch paper.
Part 1 · The idea
What is recursion?
It is enrollment day, and the line at the registrar's window is long. You are at the back, and you want to know how many people are ahead of you, but you cannot see the front. So you ask the person ahead of you: "How many are ahead of you?" He does not know either, so he asks the person ahead of him, and so on.
Nobody counted the whole line. Each person did one small job: ask the person ahead, wait for the answer, add 1. Only Ana could answer right away. This way of solving a problem is called recursion.
Recursion
Recursion is a way of solving a problem in which a method calls itself to solve a smaller version of the same problem, again and again, until the problem is simple enough to solve directly. Every recursive method has two parts:
- Base case: a situation simple enough to handle directly, without calling itself. This is where it stops.
- Recursive case: the method calls itself on a smaller part, then uses the answer.
Each part of the definition matters. This table explains them one by one, using the registrar line:
| The definition says… | In plain words | In the registrar line |
|---|---|---|
| calls itself | the method runs its own code again, as a new call | each person asks the same question |
| a smaller version of the same problem | the same question, with less work left | fewer people ahead each time |
| base case | the stopping point, solved directly | Ana: nobody ahead, so "0" |
| recursive case | call itself on the smaller part, then use its answer | ask the person ahead, then add 1 |
Notice the two directions. The question goes forward (calls start, one inside another), then the answers come back (calls finish, newest first). You will see both in every example of this lesson.
You already used recursion
In the traversals lesson, "traverse the entire left subtree" meant: do the same three tasks again, on a smaller tree. Look again at the three-task rule : the rule uses itself. For example, in pre-order:
After writing 20, we apply the same three tasks to the subtree rooted at 10 (the smaller tree made of 10 and everything below it), then to the subtree rooted at 30.
Doing the same task on a smaller part is recursion.
The same idea works on the folders in your phone, because a folder system is a tree too: a folder is a node, and the folders inside it are its children. Say you want to list every file in your Documents folder. The rule for any folder: (1) list the files in it; (2) for each folder inside, use this same rule.
It works on bigger folder trees too, like the folder tree from the Trees lesson .
Recursion in Java: a countdown
Before trees, here is a Java method that calls itself and then stops. There are no tree nodes yet, so it is easy to follow. The call countDown(3); prints 3 2 1 Go!, like a New Year countdown.
1static void countDown(int n) {
2 if (n == 0) { // base case
3 System.out.println("Go!");
4 return;
5 }
6 System.out.print(n + " ");
7 countDown(n - 1); // calls itself
8} base caserecursive case
- Base case, lines 2 to 5: when
nis 0, print Go! and stop. - Recursive case, lines 6 and 7: print
n, then call itself with a smaller number.
Step through it. The "Call stack" box in the picture lists the calls that have started but not finished. Its colors mean:
runningpaused, waitingending (returning)
Walkthrough: countDown(3)
Use Next to follow the calls going in (forward) and finishing (back). Tap a line to jump to it.
Call stack (newest on top)
countDown(3)running
Output so far:
n = 3
Our program runs countDown(3);. Java starts one call of the method, with n = 3. The box lists every call that has started but not finished, newest on top. It is called the call stack (Part 2).
1static void countDown(int n) { 2 if (n == 0) { // base case 3 System.out.println("Go!"); 4 return; 5 } 6 System.out.print(n + " "); 7 countDown(n - 1); // calls itself 8} We only wrote one call, countDown(3);. Line 7 made the others. This table sums them up:
| Call | Who calls it | Prints |
|---|---|---|
countDown(3) | we do | 3 |
countDown(2) | countDown(3), line 7 | 2 |
countDown(1) | countDown(2), line 7 | 1 |
countDown(0) | countDown(1), line 7 | Go!, then stops |
Why does it stop? Each call uses a smaller number, and at 0 it returns without calling itself again. A recursive method needs both: a base case, and calls that get closer to it each time. (Like the registrar line: each question goes to someone nearer the front.)
Part 2 · Pre-order in Java
preOrder and the call stack
Now we use recursion to traverse a tree: visit every node once, in a set order. Programs need this to print, save, copy, or delete every value. From the traversals lesson, there are three orders: pre-order (N L R), in-order (L N R), and post-order (L R N).
In Java, each order is a short recursive method. Each node is a TreeNode object, with a number (data) and two references (left, right), or null when a child is missing. Remember the TreeNode class ? Here is pre-order as a Java method:
1static void preOrder(TreeNode node) {
2 if (node == null) { return; } // stop
3 System.out.print(node.data + " "); // N
4 preOrder(node.left); // L
5 preOrder(node.right); // R
6} base caserecursive case
- Base case, line 2: if
nodeisnull(an empty spot), there is nothing to print.return;stops this call now. - Recursive case, lines 3 to 5: print this node, then use the same method on the left subtree, then on the right.
To print a whole tree, call preOrder(root);. It uses only left and right, so it works on any binary tree, not only BSTs.
How the code does N, L, R
By hand, every node had three tasks. This table matches each task to its line of code:
| By hand (traversals lesson) | In the code |
|---|---|
| N write this node | line 3: System.out.print(node.data + " "); |
| L do the whole left subtree, same rule | line 4: preOrder(node.left); runs the same method again, with the left child as the new node |
| R do the whole right subtree, same rule | line 5: preOrder(node.right); the same way |
| empty side: nothing to do | line 2: null, so stop |
Some of these lines use Java you may not have read aloud before. This table shows how to read each one:
| Java | Read it as |
|---|---|
static void preOrder(TreeNode node) | "a method named preOrder that takes one node." void: it gives back no value (it can still print). |
if (node == null) { return; } | "if there is no node here, end this call now." return; ends the call without giving back a value. |
System.out.print(node.data + " "); | "print this node's value, then a space." |
preOrder(node.left); | "start a new call of preOrder, on this node's left child. Wait for it to finish." |
What is a call stack?
When method A calls method B, Java pauses A and runs B. When B finishes, A continues from where it paused. In recursion, A and B are the same method: the call for 20 pauses while a new call for 10 runs. Java keeps a list of every unfinished call: the call stack.
Java works the same way: a new call goes on top, and only the top call runs. When it finishes, it is removed, and the call below continues where it paused. Remember the Stacks lesson? It listed "method calls" as a stack use. A new call is a push, and a finished call is a pop (last in, first out). Java does them for us.
Trace preOrder, step by step
Many preOrder calls run, one after another. To tell them apart, we give each call a short name, using the node it works on:
| Real code | Our name |
|---|---|
preOrder(root)the first call | preOrder(20)the call working on 20 |
preOrder(node.left)inside the call for 20 | preOrder(10)the call working on 10 |
preOrder(node.right)inside the call for 20 | preOrder(30)the call working on 30 |
Only for our notes
These names make the call stack pictures easier to read. You cannot type preOrder(10) in Java: preOrder needs a node, not a number.
We call preOrder(root); on a small tree: 20, with 10 on its left and 30 on its right. In the picture, the tag node shows where the running call's node points. Light teal nodes are already printed.
runningpaused, waitingending (returning)
Walkthrough: preOrder(root)
Use Next to watch the calls go on the stack and come off. Tap a line to jump to it.
Call stack (newest on top)
preOrder(20)running
Output so far:
node → 20
We call preOrder(root);. A new call starts, and its node points to 20. We name this call preOrder(20): "the call working on 20." You cannot type that in Java, because preOrder needs a node, not a number.
1static void preOrder(TreeNode node) { 2 if (node == null) { return; } // stop 3 System.out.print(node.data + " "); // N 4 preOrder(node.left); // L 5 preOrder(node.right); // R 6} Each call has its own node
All the calls have a variable named node, but each has its own. When preOrder(10) finishes, preOrder(20) continues at line 5, and its node still points to 20. return; ends one call only.
What happens at a leaf?
A leaf has no children, but its call still runs every line. It prints, then makes two calls on its empty spots, and each of those stops at line 2.
Show the call for 10, line by line
| Line | What happens |
|---|---|
2 if (node == null) | 10 is not null: keep going |
3 print(node.data) | prints 10 |
4 preOrder(node.left) | 10's left is empty: a new call starts… |
2 if (node == null) | …yes: it stops (base case) |
5 preOrder(node.right) | 10's right is empty: a new call starts… |
2 if (node == null) | …yes: it stops (base case) |
6 } | the call for 10 is finished |
The green rows are the two new calls. Each one stops at line 2.
Why is null the base case, not the leaf?
A leaf still needs a print and two more calls. A base case handles a situation without making another recursive call. In this traversal, that situation is an empty subtree (null): there is nothing to print.
Two ways a call ends. A call ends at return;, or when Java reaches the method's closing }. That is how preOrder(20) finished in the walkthrough: it never ran line 2's return;. Either way, only that call ends, and its caller continues where it made the call.
Quick check
In the walkthrough, preOrder(10) has just finished. (1) Which call continues next? (2) At which line?
Show the answer
(1) preOrder(20), the call that was paused. (2) Line 5. When a call returns, Java continues right after the place where the call was made. The call was the whole statement on line 4, so Java continues at line 5.
Now try a bigger tree. The lab below runs the same code on the starting BST. You choose when to take each step.
Call stack lab
Watch the call stack grow and shrink. preOrder(20) means the call working on node 20 (in real code, it is preOrder(root) or preOrder(node.left)).
Try an example
Color key
Part 3 · In-order and post-order
Move one line
The other two methods are preOrder with one change: the print line moves. Everything else is the same: line 2 stops at null, and the method calls itself twice.
In-order: print between the calls
1static void inOrder(TreeNode node) {
2 if (node == null) { return; } // stop
3 inOrder(node.left); // L
4 System.out.print(node.data + " "); // N
5 inOrder(node.right); // R
6}Post-order: print after both calls
1static void postOrder(TreeNode node) {
2 if (node == null) { return; } // stop
3 postOrder(node.left); // L
4 postOrder(node.right); // R
5 System.out.print(node.data + " "); // N
6}This table compares where each method prints, and what it prints for the starting BST :
| Method | Print line is… | On the starting BST, prints |
|---|---|---|
| preOrder | line 3: before both calls (N L R) | 40 20 10 30 60 50 70 |
| inOrder | line 4: between the calls (L N R) | 10 20 30 40 50 60 70 |
| postOrder | line 5: after both calls (L R N) | 10 30 20 50 70 60 40 |
These match what you get by hand.
inOrder: waiting to print
In inOrder, a node waits to print. Here is inOrder(root) on the small tree (20, with 10 on its left and 30 on its right), in four steps:
inOrder(20)callsinOrder(10)at line 3, then pauses. It has not printed yet.inOrder(10)calls its empty left side, which returns right away. Then line 4 prints 10. Its empty right side returns too.inOrder(10)is finished.inOrder(20)continues at line 4 and prints 20.- Line 5 calls
inOrder(30), which prints 30.
Console10 20 30
Step 3 is the key: 20 waits until its whole left side is done, then prints. Switch the order in the call stack lab to watch it wait on the bigger tree.
Quick check
What does postOrder(root) print for the small tree (20, with 10 on its left and 30 on its right)?
Show the answer
10 30 20. postOrder(20) calls postOrder(10): 10 prints after its empty sides. Then postOrder(30): 30 prints. Only then does line 5 of postOrder(20) print 20: last.
So far, recursion only printed a tree. Next: recursion that adds a node, using the insert from the insertion lesson .
Part 4 · Recursive insert
insertRec
In the insertion lesson, insert walked down with a loop . Now we do the same walk with recursion. One difference: the traversal methods print and give back nothing (void), but insertRec gives back a node. This table compares the two versions:
| Insertion lesson: loop | Now: recursion |
|---|---|
moves down: current = current.left; | calls itself on the smaller subtree: insertRec(node.left, value) |
stops at an empty spot (null) | base case: node == null |
connects it: current.left = new TreeNode(value); | saves what the call gives back: node.left = insertRec(…); |
Here is the full method. Lines 7 and 9 call itself:
1static TreeNode insertRec(TreeNode node,
2 int value) {
3 if (node == null) { // base case
4 return new TreeNode(value);
5 }
6 if (value < node.data) {
7 node.left = insertRec(node.left, value); // calls itself
8 } else if (value > node.data) {
9 node.right = insertRec(node.right, value); // calls itself
10 }
11 return node;
12} base caserecursive case
Two new lines appear in this method. This table shows how to read them aloud:
| Java | Read it as |
|---|---|
return node; | "end this call and give back this node to the caller." |
node.left = insertRec(node.left, value); | "run insertRec on the left child. Whatever it gives back, save it as this node's left." |
The big idea: every call returns the root of its own subtree, and the caller saves it. We call it the same way as the loop version: root = insertRec(root, 45); Step through it:
runningpaused, waitingending (returning)
Walkthrough: insertRec(root, 45)
Going down: the calls pile up. Coming back up: each call saves an answer and returns its own node.
Call stack (newest on top)
insertRec(40, 45)running
node → 40value = 45
We call root = insertRec(root, 45); The first call works on the root, 40. insertRec(40, 45) is our name for "the call working on 40."
1static TreeNode insertRec(TreeNode node, 2 int value) { 3 if (node == null) { // base case 4 return new TreeNode(value); 5 } 6 if (value < node.data) { 7 node.left = insertRec(node.left, value); // calls itself 8 } else if (value > node.data) { 9 node.right = insertRec(node.right, value); // calls itself10 }11 return node;12} The key step: at 50
Only one call changes the tree. The call for 50 was paused at line 7, and the call below it just gave back node 45. The picture shows the tree before and after line 7 finishes:
This line runs: node.left = insertRec(node.left, value);
before: the call below returned 45
after: 50's left is 45
Why "node.left = …"?
Most calls just save the same child again. Only at the bottom does a call save the new node. Without node.left =, the new node is never connected (Part 6, Mistake 3).
The calls for 60 and 40 do the same two steps (save, then return), but they save the child that was already there, so nothing changes.
Show the way back up in one table
| Call | What it does | Returns |
|---|---|---|
insertRec(null, 45) | creates node 45 | 45 |
insertRec(50, 45) | saves 45 as 50's left: connects 45 | 50 |
insertRec(60, 45) | saves 50 as 60's left again: no change | 60 |
insertRec(40, 45) | saves 60 as 40's right again: no change | 40 |
Empty tree and duplicates
Empty tree: root = insertRec(null, 7); node is null, so line 4 returns a new node 7, and root = saves it. Now 7 is the root.
Value already there: insertRec(root, 30) goes 40 → 20 → 30. At 30 the value is equal, so neither line 7 nor line 9 runs. Line 11 returns node 30 unchanged. Nothing is added (our BSTs skip duplicates).
Part 5 · Other books
Same code, different names
Books, websites, and exams often write the same methods with other names. Only the names change, never the steps. Switch between the two versions below and watch which words change. Then tap a row of the table to find that name in the code.
Switch the names
Switch between our names and the names many books use. Tap a row of the table to find that name in the code (tap it again to clear).
| Our lessons | Many books |
|---|---|
TreeNode | Node |
data | key |
value | key |
node | root |
preOrder | preorderRec |
static void preOrder(TreeNode node) {
if (node == null) { return; }
System.out.print(node.data + " ");
preOrder(node.left);
preOrder(node.right);
}
static TreeNode insertRec(TreeNode node, int value) {
if (node == null) {
return new TreeNode(value);
}
if (value < node.data) {
node.left = insertRec(node.left, value);
} else if (value > node.data) {
node.right = insertRec(node.right, value);
}
return node;
} Notice that data and value both become key: books use one word for "the number we store or look for."
Books also check for null in another way. The two versions below compare our way with the other common way:
Our way
static void preOrder(TreeNode node) {
if (node == null) { return; } // stop
System.out.print(node.data + " "); // N
preOrder(node.left); // L
preOrder(node.right); // R
}Another common way
void preorderRec(Node root) {
if (root != null) {
System.out.print(root.key + " ");
preorderRec(root.left);
preorderRec(root.right);
}
}Both do the same thing: when the node is null, nothing is printed and the call ends. The second version puts the work inside if (root != null) instead of returning early.
Inside a class
Books often put the methods inside a BinarySearchTree class, with the root stored in the class:
class BinarySearchTree {
Node root;
void insert(int key) {
root = insertRec(root, key);
}
Node insertRec(Node root, int key) {
if (root == null) {
return new Node(key);
}
if (key < root.key)
root.left = insertRec(root.left, key);
else if (key > root.key)
root.right = insertRec(root.right, key);
return root;
}
} Use it like this:
BinarySearchTree bst = new BinarySearchTree();
bst.insert(50); bstis our variable referring to aBinarySearchTreeobject.insertdoesroot = insertRec(root, key);for you.- The logic is the same as our
insertRec. You will see this class again in "Find the 4 bugs" (Part 6).
Careful with "root"
In these methods, root is just the parameter name. It means "the root of this subtree", not always the top of the whole tree.
Part 6 · Watch out
Three common mistakes
These three mistakes come up often in quizzes and lab work. For each one, read the situation, look at the picture, then read the fix. After them, find the bugs in a class, then write a recursive method of your own in Java.
Mistake 1 · preOrder: removing the null check
The situation: a student thinks line 2 is extra, deletes if (node == null) { return; }, and runs preOrder(root) on the small tree:
static void preOrder(TreeNode node) {
System.out.print(node.data + " ");
preOrder(node.left);
preOrder(node.right);
} ✗ What happens20 10, then a crash
✓ With line 220 10 30
What went wrong: 10 is a leaf, but its call still runs line 4 on its empty left spot (yellow box). That new call gets null, and line 3 tries to read node.data. There is no node, so Java stops with a NullPointerException.
The fix: keep the base case first. Every call on an empty spot must stop before it touches node.data.
Mistake 2 · inOrder: calling the wrong method
The situation: a student copies preOrder to make inOrder, moves the print line, but forgets to rename one call: line 5 still says preOrder(node.right);.
1static void inOrder(TreeNode node) {
2 if (node == null) { return; }
3 inOrder(node.left);
4 System.out.print(node.data + " ");
5 preOrder(node.right); // wrong name
6} ✗ Prints10 20 30 40 60 50 70
✓ Right in-order10 20 30 40 50 60 70
What went wrong: every right subtree is printed by preOrder, so it comes out in pre-order: 60 before 50 (red nodes). The output is a mix of two orders, and it is no longer sorted.
The fix: inside inOrder, both calls must be inOrder. A recursive method calls itself.
Mistake 3 · insertRec: forgetting node.left =
The situation: a student writes line 7 as insertRec(node.left, value); and inserts 45 into the starting BST:
if (value < node.data) {
insertRec(node.left, value); // wrong: result not saved
} The buggy line runs: insertRec(node.left, value);
before: the call below returned 45
after: 50's left is still empty
What went wrong: the bottom call creates node 45 and gives it back, but nobody saves it. 50's left is still empty, and node 45 is lost. The tree did not change at all.
The fix: always save the result: node.left = insertRec(node.left, value); Compare with the key step at 50 .
Find the 4 bugs
This class should build a BST and print its in-order. It has 4 bugs. Find each line and fix it.
1class Node {
2 int key;
3 Node left, right;
4 Node(int item) {
5 key = item;
6 left = right = null; // both become null
7 }
8}
9class BinarySearchTree {
10 Node root;
11 void insert(int key) {
12 insertRec(root, key);
13 }
14 Node insertRec(Node root, int key) {
15 if (root == null) {
16 return null;
17 }
18 if (key < root.key)
19 root.left = insertRec(root.left, key);
20 else if (key > root.key)
21 root.right = insertRec(root.left, key);
22 return root;
23 }
24 void inorderRec(Node root) {
25 if (root == null) {
26 inorderRec(root.left);
27 System.out.print(root.key + " ");
28 inorderRec(root.right);
29 }
30 }
31} Show the answer
For self-study, not for grading.
- Line 12: the result is not saved. Fix:
root = insertRec(root, key); - Line 16: the base case must create the node. Fix:
return new Node(key); - Line 21: the right side must use the right child. Fix:
root.right = insertRec(root.right, key); - Line 25: the work must run when the node is not null. Fix:
if (root != null)
With the fixes, inserting 50, 30, 70, 20, 40 and printing the in-order gives 20 30 40 50 70.
Your coding task: the height of a tree
So far, our recursive methods print (preOrder) or give back a node (insertRec). This one gives back a number: the height of the tree. From the Trees lesson, height is the number of edges on the longest path from a node down to a leaf. A leaf has height 0.
Think of it the recursive way. The height of a node is 1 more than the height of its taller subtree: one edge down to that child, plus that child's own height. An empty spot (null) is the base case. It gets −1, so a leaf gets 1 + (−1) = 0.
Write height in about 10 lines where it says YOUR CODE. main builds the starting BST with insertRec. Run it: each printed line must match its comment.
1public class Main {
2 // Returns the height of the tree: the number of edges
3 // on the longest path from node down to a leaf.
4 static int height(TreeNode node) {
5 // YOUR CODE:
6 // 1. Base case: if node is null (an empty spot), return -1.
7 // (Then a leaf gets 1 + (-1) = 0, as in the Trees lesson.)
8 // 2. Recursive case: find the height of the left subtree
9 // and the height of the right subtree.
10 // 3. Return 1 + the bigger of the two.
11
12 return 0;
13 }
14
15 // Given: insertRec from this lesson.
16 static TreeNode insertRec(TreeNode node, int value) {
17 if (node == null) {
18 return new TreeNode(value);
19 }
20 if (value < node.data) {
21 node.left = insertRec(node.left, value);
22 } else if (value > node.data) {
23 node.right = insertRec(node.right, value);
24 }
25 return node;
26 }
27
28 public static void main(String[] args) {
29 int[] values = {40, 20, 60, 10, 30, 50, 70};
30 TreeNode root = null;
31 for (int i = 0; i < values.length; i++) {
32 root = insertRec(root, values[i]);
33 }
34 System.out.println(height(root)); // expected: 2
35 System.out.println(height(root.left)); // expected: 1
36 System.out.println(height(root.left.left)); // expected: 0 (a leaf)
37
38 root = insertRec(root, 45);
39 System.out.println(height(root)); // expected: 3
40
41 TreeNode chain = null;
42 for (int v = 10; v <= 50; v = v + 10) {
43 chain = insertRec(chain, v);
44 }
45 System.out.println(height(chain)); // expected: 4
46 System.out.println(height(null)); // expected: -1
47 }
48}
49
50class TreeNode {
51 int data;
52 TreeNode left;
53 TreeNode right;
54
55 TreeNode(int value) {
56 data = value;
57 left = null;
58 right = null;
59 }
60} ▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.
Show one possible answer
1static int height(TreeNode node) {
2 if (node == null) {
3 return -1;
4 }
5 int leftH = height(node.left);
6 int rightH = height(node.right);
7 if (leftH > rightH) {
8 return 1 + leftH;
9 } else {
10 return 1 + rightH;
11 }
12} Output, one per line: 2, 1, 0, 3, 4, -1.
For the starting BST, the answers come back from the bottom up. Each leaf (10, 30, 50, 70) gets 1 + (−1) = 0. Then 20 and 60 each get 1 + 0 = 1. Last, 40 gets 1 + 1 = 2. Like post-order, a node finishes only after both of its subtrees are done.
After 45 is inserted under 50, the longest path is 40, 60, 50, 45: 3 edges, so the height is 3. The chain 10, 20, 30, 40, 50 has 4 edges. Math.max(leftH, rightH) also picks the bigger number, if you know it. The AVL lesson uses it.
A common mistake is returning 0 for null. Then every answer is 1 too big: the program prints 3 2 1 4 5 0 (one per line), and a leaf gets height 1. That counts nodes, not edges. Some books count that way, but in this course a leaf is 0.
Keep this method in mind. The AVL lesson needs the height of every node, again and again, so it stores each height inside the node instead of calling height each time.
Part 7 · Check yourself
Self-check
Try it yourself (on scratch paper)
- What does
countDown(4)print? What would happen withcountDown(-1)? Why? - In
preOrder, afterpreOrder(node.left)returns, which line runs next? - Write what
preOrder,inOrder, andpostOrderprint for Tree S1 (from the traversals lesson): - Insert 55 into the starting BST with
insertRec. List the calls going down. Then write what each call returns. - Fill in the blanks A to D so the method prints the post-order:
static void postOrder(TreeNode node) { if (node == ______) { return; } // A postOrder(______); // B postOrder(______); // C System.out.print(______ + " "); // D }
Show the answers
For self-study, not for grading.
- 4 3 2 1 Go!
countDown(-1)goes to -2, -3, -4… and never reaches 0, so the calls keep piling up on the call stack until Java crashes (with aStackOverflowError). "It never reaches 0" is the key idea. - Line 5:
preOrder(node.right); - Pre-order: 8 3 1 6 4 7 10 14 13
In-order: 1 3 4 6 7 8 10 13 14
Post-order: 1 4 7 6 3 13 14 10 8 - Down: 40 → 60 → 50 →
null(50's right spot).Call Returns insertRec(null, 55)new node 55 insertRec(50, 55)50 (50's right now refers to node 55) insertRec(60, 55)60 insertRec(40, 55)40 - A:
null· B:node.left· C:node.right· D:node.data
The quiz below is not graded either. Tap an answer to see if you got it, and read why.
-
What is recursion?
Recursion = a method calling itself on a smaller problem, until it reaches a base case.
-
In the registrar line from Part 1, who is the base case?
Ana can answer right away ("0") without asking anyone. That is a base case: solved directly, with no further asking (no further call).
-
What is the base case of
preOrder?The base case handles a situation without another recursive call. A leaf still prints and makes two calls; the empty spot (
null) just returns. -
In
preOrder, afterpreOrder(node.left)returns, which line runs next?When a call returns, Java continues where it made the call: the line after it, line 5.
-
Which method prints the node between its two recursive calls?
inOrder: left call, print, right call (L N R).
-
What happens with
countDown(-1)?-1, -2, -3… never reaches the base case (0). The call stack grows until Java throws a
StackOverflowError. -
While inserting 45 into the starting BST, what does
insertRec(60, 45)return?Every call returns the root of its own subtree. The subtree that starts at 60 still starts at 60.
-
What is wrong with
insertRec(node.left, value);(nonode.left =)?The bottom call creates the node and returns it, but nobody stores it. Always save the result:
node.left = insertRec(node.left, value);
Score: 0 / 8
Key takeaways
- Recursion: a method calls itself on a smaller problem, and stops at a base case (like the registrar line ).
- The call stack keeps every unfinished call; a finished call lets its caller continue where it paused.
- preOrder, inOrder, and postOrder differ only in where the print line is.
- For traversals, the base case is
node == null. - insertRec: every call returns the root of its own subtree; always save it (
node.left = …).
Next lesson: BST deletion.
References
- Programiz, "Java Recursion" and "Binary Search Tree." programiz.com
- GeeksforGeeks, "Insertion in Binary Search Tree." geeksforgeeks.org
- A. Bhargava, Grokking Algorithms, 2nd ed. Manning, 2024, chapter on recursion.