ma'am audrey's lessons

ITC 120 › Midterm › Trees and BSTs

Trees and BSTs · Lesson 3 of 6

BST Insertion

This lesson teaches:Algorithm

Adding a value by hand, then in Java: search for the value, and where the search falls off the tree, attach the new node.

By the end, you can:

  1. insert values into a BST by hand and draw the tree after each one;
  2. explain the two actions in the code: move current and connect a new node;
  3. trace insert, including a value that is already there and an empty tree;
  4. build a whole BST from a list of values, spot three common mistakes, and write the insert loop yourself.

You need: the BST lesson, and scratch paper.

Part 1 · By hand

Insertion by hand

A new student enrolls, and their ID must be added to the tree in a place where search will find it later. Putting it anywhere would break the BST rule. So: search for the value; where the search reaches null, attach the new node.

Joynew enrolleeI just enrolled!DOrSU RegistrarAdd new studentStudent ID2025180Addbehind the screen: where does 2025180 go?20251502025075202523020250102025180empty spot: it goes here!1. 2025180 > 2025150 → go right2. 2025180 < 2025230 → go left: empty!
The registrar program does not look for "any free spot." It asks the same questions as a search, and the first empty spot it reaches is the only correct place.

Insert asks the same questions as . Only the endings are different, as this table shows:

CheckSearchInsert
Tree empty?return falsethe new node becomes the root
Equal?foundalready there: skip
Smaller?go leftleft empty? attach there. If not, go left
Larger?go rightright empty? attach there. If not, go right

Put together, inserting by hand follows a short checklist. It is the search checklist with new endings:

Insert checklist

Empty tree? The new node becomes the root. Otherwise, start at the root and compare the new value with the current node:

  1. equal? → already in the tree. Skip it. Stop.
  2. smaller? → look left. Empty? Attach it there and stop. Not empty? Go left, back to 1.
  3. larger? → look right. Empty? Attach it there and stop. Not empty? Go right, back to 1.

The new value always ends up as a new leaf. No other node moves.

Try it in the lab below. Each example shows the walk down, one check at a time. The chip at the top left of the tree says what this step did; the tree shows where we are now.

Insert lab

Watch the “current” label. The tree only changes at the very last step. Then insert your own numbers: each one is added to the same tree.

Try an example

Or try your own number

Color key
current checked new node

Part 2 · Reading the code

The insert method

Here is one way to write insert in Java. It uses a loop, like the search method, and builds new nodes with . Read through it once first. Do not worry if it is not clear yet: the rest of this part explains it, using the line numbers on the left.

 1static TreeNode insert(TreeNode root,
 2                       int value) {
 3  if (root == null) {
 4    return new TreeNode(value);
 5  }
 6  TreeNode current = root;
 7  while (true) {
 8    if (value == current.data) {
 9      return root;
10    } else if (value < current.data) {
11      if (current.left == null) {
12        current.left = new TreeNode(value);
13        return root;
14      }
15      current = current.left;
16    } else {
17      if (current.right == null) {
18        current.right = new TreeNode(value);
19        return root;
20      }
21      current = current.right;
22    }
23  }
24}

Before reading it line by line, meet the names it uses. This table lists each one:

NameMeaning
rootrefers to the top node; null when the tree is empty
valuethe number we want to insert; it stays the same during the call
currentrefers to the node we are checking now; it moves down the tree
current.datathe number stored in that node
current.left, current.rightthat node's children (either can be null)

The two actions

Look at lines 12, 15, 18, and 21 in the code above. Each of them does one of only two actions: it moves current, or it connects a new node. Learn these two actions first, and the rest of the code is easy to follow. Each action has a picture pair: the tree before the line runs (left) and after it runs (right). The arrow between them means "after this line runs." On a phone, the pictures stack, and the arrow points down.

Action 1: Move current (line 15 or 21)

This line runs: current = current.left;

before: current is at 60

102030405060current70
→after

after: same tree, current moved to 50

1020304050current6070

current moves to a child that already exists. The tree does not change. Think of current as a finger pointing at one node: moving the finger does not change the tree. (Line 21 is the same, going right: current = current.right;)

Action 2: Connect a new node (line 12 or 18)

This line runs: current.left = new TreeNode(45);

before: 50's left is empty

1020304050current6070
→after

after: 45 added, current stayed at 50

102030404550current6070

A new node is connected through the empty left of the node current refers to. The tree changes, but current does not move. (Line 18 is the same, on the right: current.right = new TreeNode(value);)

How to read these lines aloud

Java runs the right side of = first, then stores the result in the left side. A dot (.) means "go into this node and use its part." So read each line from right to left, like this:

LineRead it as
current = current.left;Take the left child of the current node, and make it the new current node.
current = current.right;Take the right child of the current node, and make it the new current node.
current.left = new TreeNode(45);Make a new node holding 45, and store it as the left child of the current node.

Spot the difference: if the left side is just current, only the arrow moves. If the left side is current.left or current.right, a link inside the tree changes.

Step through the code

Now watch the whole method insert 45 into the starting tree. The dashed boxes are the empty (null) spots; the yellow one is where 45 will go.

insert(root, 45), step by step

Tap any line to see what it does, or use Next to go through the code in order.

10203040506070

value = 45

We call insert(root, 45). The method gets the tree (through root) and the value to add: 45. The small dashed boxes are the empty (null) child spots.

Step 1 of 14
 1static TreeNode insert(TreeNode root, 2                       int value) { 3  if (root == null) { 4    return new TreeNode(value); 5  } 6  TreeNode current = root; 7  while (true) { 8    if (value == current.data) { 9      return root;10    } else if (value < current.data) {11      if (current.left == null) {12        current.left = new TreeNode(value);13        return root;14      }15      current = current.left;16    } else {17      if (current.right == null) {18        current.right = new TreeNode(value);19        return root;20      }21      current = current.right;22    }23  }24}

What each part does

Here is the same method as a short summary, part by part:

  • Lines 3–5: empty tree? The new node is the root.
  • Line 6: start at the root, like search.
  • Lines 8–9: equal? The value is already there. Stop.
  • Lines 11–14, 17–20: empty spot on that side? Connect the new node there, then stop.
  • Lines 15, 21: not empty? Move to that child, then check again.

return exits the whole method right away, not just the if or the loop.

Part 3 · Calling insert

root = insert(root, 45);

Java first runs insert(root, 45): it adds 45, then gives back the top of the tree. Then root = saves what it gave back.

Does 45 become the root?

No! 45 is added at the bottom (as 50's left child, as you saw in ). The method gives back the same top node it was given (40), and root = just saves that same node again.

Then why save it? When the tree is empty, the new node becomes the top. The method gives back a new top, and we must save it. Try "Empty tree, insert 7" in the lab.

Build a whole tree with a loop

To build a whole tree, call insert once for each value. Step through the loop and watch the first pass: that is the empty-tree case, where root = really matters.

Building a BST with a loop

Use Next to go through the loop one pass at a time, or tap a line.

nothing built yet

The values to insert, in this order.

Step 1 of 10
 1int[] values = {40, 20, 60, 10, 30, 50, 70}; 2TreeNode root = null; 3for (int i = 0; i < values.length; i++) { 4  root = insert(root, values[i]); 5}

The order of insertion decides the shape. Here are the same four values, inserted in two different orders:

Order: 30, 10, 40, 20

10203040

Short and wide: height 2.

Order: 10, 20, 30, 40 (sorted)

10203040

A chain: height 3. Search becomes as slow as a linked list.

Inserting 10, 20, 30, … 70 in sorted order would make an even longer chain. Fixing this is the job of AVL trees, later in the course.

Part 4 · Watch out

Three common mistakes

These three mistakes are the most common ones in student code. Each one compiles and runs with no error message, but the tree comes out wrong. For each one, read the situation, look at what the buggy line does to the tree, then read the fix.

Mistake 1: Forgetting root =

The situation: we start with an empty tree (TreeNode root = null;) and want to insert 25. A student writes insert(root, 25); with no root = in front.

The buggy line runs: insert(root, 25);

before: empty tree

rootnull
→after

after: root is still null, 25 is lost

rootnull25lost

What went wrong: inside insert, the tree is empty, so line 4 makes node 25 and returns it. But the caller never saves what was returned. root is still null, and node 25 is lost. The tree is still empty.

The fix: save what insert gives back.

This line runs: root = insert(root, 25);

before: empty tree

rootnull
→after

after: root refers to 25

root25

Why is this bug sneaky? On a tree that already has nodes, insert returns the same root, so forgetting root = seems to work. It only breaks on the very first insert, into an empty tree. So always write root = insert(root, value);

Mistake 2: Moving instead of connecting

The situation: we insert 45 into the starting tree. current has walked down 40 → 60 → 50, and 50's left is empty (the yellow box), so 45 belongs there. But the student wrote current = new TreeNode(45); on line 12.

The buggy line runs: current = new TreeNode(45);

before: current is at 50

1020304050current6070
→after

after: 50's left is still empty

1020304050607045current

What went wrong: the left side is just current, so this line only moves the arrow. Node 45 is made, and current now refers to it, but no node in the tree links to 45. 50's left is still null. When the method returns, current disappears, and 45 is lost with it.

The fix: connect it through the parent: current.left = new TreeNode(45); Compare with .

Mistake 3: Connecting without checking for null

The situation: we insert 25 into the starting tree. At 40, 25 < 40, so we look left. The student skipped the if (current.left == null) check and connects right away.

The buggy line runs: current.left = new TreeNode(25);

before: 40's left holds 20

10203040current506070
→after

after: 25 replaced 20

2540506070

cut off, lost:

102030

What went wrong: 40's left was not empty: it held 20, and 10 and 30 hang below 20 (the yellow nodes). Storing 25 in 40's left replaces the link to 20. Now no node links to 20, so 20, 10, and 30 are cut off from the tree. Three values are gone, and the BST is wrong.

The fix: always check for null first. If the spot is taken, move instead (line 15). Done right, 25 goes 40 → 20 → 30 and becomes the left child of 30.

Part 5 · Your turn

Practice

Problems 1 to 3 are on paper. The coding task at the end runs in Java.

Problem 1: trace table

Using (40, 20, 60, 10, 30, 50, 70), make a trace table for insert(root, 55): one row each time the loop checks a node.

Show the answer
current.dataCompareChild on that sideThen
4055 > 40right = 60move right
6055 < 60left = 50move left
5055 > 50right = nullconnect 55; return root

Problem 2: build and answer

Start with an empty tree. Insert, in this order: 50, 30, 75, 25, 40, 35, 90, 80, 95

Draw it on paper first. Then build it below, placing the values in the same order, and tap Check.

Build the final BST

Drag a value onto a dashed spot. On a phone, you can also tap a value, then tap a spot. Tap a leaf to take it back.

  1. Draw the final BST.
  2. Which node is the parent of 35?
  3. If we then call insert(root, 85), which nodes does current visit? Where is 85 connected?
  4. What happens if we call insert(root, 40)?
Show the answer
253035405075809095
  1. See the drawing.
  2. 40 (35 < 50: left. 35 > 30: right. 35 < 40: left. Empty, so 35 is connected there.)
  3. 50, 75, 90, 80. 85 > 80 and 80's right is empty, so 85 becomes the right child of 80.
  4. Nothing changes. current visits 50, 30, 40; 40 is equal, so it is skipped.

Problem 3: find the bug

A classmate inserted 45, but 45 never appears in the tree. Which line is wrong?

 1} else if (value < current.data) {
 2  if (current.left == null) {
 3    current = new TreeNode(value);
 4    return root;
 5  }
 6  current = current.left;
 7}
Show the answer

Line 3: current = new TreeNode(value); only moves current; the new node is never connected. It should be current.left = new TreeNode(value);

Your coding task: insert with a parent pointer

Our insert looks before each move: is the spot on that side empty? Many books write insert another way. They walk down until current falls off the tree (becomes null), and they keep a second variable, parent, one step behind it. When current is null, parent is the node that gets the new child.

Why a second variable? Remember : current = new TreeNode(…) only moves the arrow. Once current is null, it cannot connect anything. parent still refers to a real node, so parent.left = newNode; really connects the new node.

Finish insert where it says YOUR CODE: about 15 short lines, and many of them are just braces. main builds with the same for loop as before, then inserts 55, 25, and 30 again. Run it: each printed line must match its comment.

 1public class Main {
 2  // Inserts value into the BST and returns the root.
 3  // Duplicates are skipped.
 4  static TreeNode insert(TreeNode root, int value) {
 5    TreeNode newNode = new TreeNode(value);
 6    if (root == null) {
 7      return newNode;
 8    }
 9    TreeNode parent = null;
10    TreeNode current = root;
11    // YOUR CODE:
12    // 1. Walk down. While current is not null:
13    //    - if value equals current.data, return root (skip it);
14    //    - set parent = current, then move current
15    //      left or right.
16    // 2. Now current is null, and parent is the last node
17    //    visited. Connect newNode as parent's left child
18    //    if value < parent.data, otherwise as its right child.
19
20    return root;
21  }
22
23  public static void main(String[] args) {
24    int[] values = {40, 20, 60, 10, 30, 50, 70};
25    TreeNode root = null;
26    for (int i = 0; i < values.length; i++) {
27      root = insert(root, values[i]);
28    }
29    System.out.println(root.data);                   // expected: 40
30    System.out.println(root.right.left.data);        // expected: 50
31
32    root = insert(root, 55);
33    System.out.println(root.right.left.right.data);  // expected: 55
34    root = insert(root, 25);
35    System.out.println(root.left.right.left.data);   // expected: 25
36    root = insert(root, 30);                         // a duplicate
37    System.out.println(root.left.right.right == null); // expected: true
38  }
39}
40
41class TreeNode {
42  int data;
43  TreeNode left;
44  TreeNode right;
45
46  TreeNode(int value) {
47    data = value;
48    left = null;
49    right = null;
50  }
51}

▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.

Before you write the loop, the starter prints 40 and then stops with a NullPointerException: only the first value (40) was kept, so root.right is null. That is expected.

Show one possible answer
 1static TreeNode insert(TreeNode root, int value) {
 2  TreeNode newNode = new TreeNode(value);
 3  if (root == null) {
 4    return newNode;
 5  }
 6  TreeNode parent = null;
 7  TreeNode current = root;
 8  while (current != null) {
 9    if (value == current.data) {
10      return root;
11    }
12    parent = current;
13    if (value < current.data) {
14      current = current.left;
15    } else {
16      current = current.right;
17    }
18  }
19  if (value < parent.data) {
20    parent.left = newNode;
21  } else {
22    parent.right = newNode;
23  }
24
25  return root;
26}

Output, one per line: 40, 50, 55, 25, true.

Here is insert(root, 55) again, one row each time the loop checks a node. Compare it with your Problem 1 table:

current.dataCompareThen parentThen current
4055 > 404060 (go right)
6055 < 606050 (go left)
5055 > 5050null (50 has no right child)

The loop stops. 55 > 50, so parent.right = newNode; and 55 becomes the right child of 50, the same spot as in Problem 1.

The last test inserts 30, which is already in the tree. The loop reaches 30, finds it equal, and returns root, so nothing is connected. Without that check, 30 is not smaller than 30, so it would go right, and a second 30 would hang under the first. Then root.left.right.right would not be null, and the line would print false.

A common mistake is writing current = newNode; after the loop. That is Mistake 2 again. The program prints 40, then crashes with a NullPointerException. Only the first value was kept (40, the insert into the empty tree), so root.right is null.

Which version is better? Both visit the same nodes and do the same work. Ours connects inside the loop. This one connects in one place, after the loop. Use whichever you find easier to read.

Part 6 · Check yourself

Self-check

Not graded. Tap an answer to see if you got it, and read why.

  1. Where does a new value go in a BST?

  2. current refers to node 50, and 50's left is empty. Which line connects 45?

  3. After current.left = new TreeNode(45);, what does current refer to?

  4. What does insert(root, 30) do if 30 is already in the tree?

  5. Why do we write root = insert(root, value);?

  6. Does root = insert(root, 45); make 45 the root?

Score: 0 / 6

Key takeaways

  1. Insert = search for the value, then attach it at the null spot. It becomes a new leaf.
  2. current = current.left; moves; current.left = new TreeNode(v); connects.
  3. Always check for null before connecting, or you cover up a node.
  4. Always write root = insert(root, value);
  5. Insertion order decides the shape of the tree.

Next lesson: Tree traversals. (In the Recursion lesson, you will also see a recursive version of insert.)

References

  1. Programiz, "Binary Search Tree (BST)." programiz.com/dsa/binary-search-tree
  2. Oracle, "TreeSet (Java SE 21 & JDK 21)," Java API documentation.