ma'am audrey's lessons

ITC 120 › Midterm › Trees and BSTs

Trees and BSTs · Lesson 5 of 6

Recursion and Traversal Code

This lesson teaches:Technique

A method that calls itself, and how it walks a tree.

By the end, you can:

  1. point out the base case and the recursive case in a recursive method;
  2. draw the call stack and say where Java continues after a call finishes;
  3. trace preOrder, inOrder, and postOrder in Java and write their output;
  4. trace the recursive insert, read the same code written with other names, and write a recursive height method.

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.

Going forward: each person asks the one aheadRegistrarHow many are in front of you?AnaBeaMigsYouNo one's infront of me.There are 2 peoplein front of me.There's 1 personin front of me.Hm, so there mustbe 3 people infront of me!Coming back: each person adds 1 to the answer
The question goes forward until it reaches Ana, who has nobody ahead. Her answer (no one, so 0) comes back, and each person adds 1 before passing it on. Each speech bubble matches its speaker's shirt.

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 wordsIn the registrar line
calls itselfthe method runs its own code again, as a new calleach person asks the same question
a smaller version of the same problemthe same question, with less work leftfewer people ahead each time
base casethe stopping point, solved directlyAna: nobody ahead, so "0"
recursive casecall itself on the smaller part, then use its answerask 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 rule uses itself. For example, in pre-order:

102030
The left subtree of 20 is a smaller tree: just 10 (light teal). The right subtree is just 30 (gold).

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.

Documentsresume.pdfhas folders inside:recursive caseITC120Lesson1.pdfLesson2.pdfPhotosselfie.jpgno folders inside:base caseno folders inside:base case
Documents has folders inside, so it uses the same rule on ITC120 and on Photos (recursive case). They have no folders inside: list their files, and that folder is done (base case).

It works on bigger folder trees too, like .

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 n is 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)

  1. 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).

Step 1 of 12
 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:

CallWho calls itPrints
countDown(3)we do3
countDown(2)countDown(3), line 72
countDown(1)countDown(2), line 71
countDown(0)countDown(1), line 7Go!, 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 ? 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 node is null (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 nodeline 3: System.out.print(node.data + " ");
L do the whole left subtree, same ruleline 4: preOrder(node.left); runs the same method again, with the left child as the new node
R do the whole right subtree, same ruleline 5: preOrder(node.right); the same way
empty side: nothing to doline 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:

JavaRead 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.

Your tasks (newest on top)Answer the doordoing nowPhone callpausedCooking adobopausedFinish the top task first. Then the one below itcontinues where you paused it.
You are cooking adobo. Your phone rings, so you pause cooking. Then someone knocks. You answer the door first (the newest task), then finish the call, then go back to cooking.

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 codeOur 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.

1020node30

Call stack (newest on top)

  1. 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.

Step 1 of 13
 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
LineWhat 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
the running call's node paused calls' nodes

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 :

MethodPrint line is…On the starting BST, prints
preOrderline 3: before both calls (N L R)40 20 10 30 60 50 70
inOrderline 4: between the calls (L N R)10 20 30 40 50 60 70
postOrderline 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:

  1. inOrder(20) calls inOrder(10) at line 3, then pauses. It has not printed yet.
  2. inOrder(10) calls its empty left side, which returns right away. Then line 4 prints 10. Its empty right side returns too.
  3. inOrder(10) is finished. inOrder(20) continues at line 4 and prints 20.
  4. 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 .

Part 4 · Recursive insert

insertRec

In the insertion lesson, . 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: loopNow: 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(…);
10203040506070
Both versions take this path to insert 45: 40, 60, 50, then 50's empty left spot.

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:

JavaRead 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.

10203040node506070

Call stack (newest on top)

  1. 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."

Step 1 of 13
 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

1020304050node607045returned
→after

after: 50's left is 45

102030404550node6070

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
CallWhat it doesReturns
insertRec(null, 45)creates node 4545
insertRec(50, 45)saves 45 as 50's left: connects 4550
insertRec(60, 45)saves 50 as 60's left again: no change60
insertRec(40, 45)saves 60 as 40's right again: no change40

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 lessonsMany books
TreeNodeNode
datakey
valuekey
noderoot
preOrderpreorderRec
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);
  • bst is our variable referring to a BinarySearchTree object.
  • insert does root = 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);
}
102030

✗ 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}
10203040506070

✗ 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

1020304050node607045returned
→after

after: 50's left is still empty

1020304050node607045lost

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 .

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.

10h=020h=130h=040h=250h=060h=170h=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)

  1. What does countDown(4) print? What would happen with countDown(-1)? Why?
  2. In preOrder, after preOrder(node.left) returns, which line runs next?
  3. Write what preOrder, inOrder, and postOrder print for Tree S1 (from the traversals lesson):
    134678101314
  4. Insert 55 into with insertRec. List the calls going down. Then write what each call returns.
  5. 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.

  1. 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 a StackOverflowError). "It never reaches 0" is the key idea.
  2. Line 5: preOrder(node.right);
  3. 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
  4. Down: 40 → 60 → 50 → null (50's right spot).
    CallReturns
    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
  5. 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.

  1. What is recursion?

  2. In the registrar line from Part 1, who is the base case?

  3. What is the base case of preOrder?

  4. In preOrder, after preOrder(node.left) returns, which line runs next?

  5. Which method prints the node between its two recursive calls?

  6. What happens with countDown(-1)?

  7. While inserting 45 into the starting BST, what does insertRec(60, 45) return?

  8. What is wrong with insertRec(node.left, value); (no node.left =)?

Score: 0 / 8

Key takeaways

  1. Recursion: a method calls itself on a smaller problem, and stops at a base case (like ).
  2. The call stack keeps every unfinished call; a finished call lets its caller continue where it paused.
  3. preOrder, inOrder, and postOrder differ only in where the print line is.
  4. For traversals, the base case is node == null.
  5. insertRec: every call returns the root of its own subtree; always save it (node.left = …).

Next lesson: BST deletion.

References

  1. Programiz, "Java Recursion" and "Binary Search Tree." programiz.com
  2. GeeksforGeeks, "Insertion in Binary Search Tree." geeksforgeeks.org
  3. A. Bhargava, Grokking Algorithms, 2nd ed. Manning, 2024, chapter on recursion.