First exams 2027 · HL only

B4.1.4 · Fundamentals of ADTs

Binary search trees

A BST keeps a "smaller goes left, bigger goes right" rule at every single node, which is the whole reason it can be searched so quickly.

HL only Typically 2–3 marks · AO2 Command term: explain

Explainer

Think of the number-guessing game "higher or lower." Every guess splits the remaining possibilities roughly in half: too high, look lower; too low, look higher. A binary search tree stores data so that searching it works exactly the same way.

Df

A node in a binary search tree holds one value and up to two references: a pointer to a left child and a pointer to a right child. Every node in a left subtree holds a smaller value than its parent; every node in a right subtree holds a larger one. A node with no children is called a leaf.

That single rule, smaller left, larger right, applied at every node, is what makes a BST searchable in roughly O(log n) steps instead of checking every value one by one. It also means inserting a new value is naturally recursive: compare against the current node, then repeat the exact same comparison one level down, left or right, until an empty spot is found.

# Node holds a value and up to two children
class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

# Recursive insertion: compare, then repeat one level down
def insert(node, value):
    if node is None:
        return Node(value)
    if value < node.value:
        node.left = insert(node.left, value)
    else:
        node.right = insert(node.right, value)
    return node

# Recursive search: same left/right comparison, no insertion
def search(node, value):
    if node is None:
        return False       # ran out of tree: not found
    if value == node.value:
        return True        # found it
    if value < node.value:
        return search(node.left, value)
    return search(node.right, value)

# Preorder: visit the node BEFORE its children
def preorder(node):
    if node is not None:
        print(node.value)
        preorder(node.left)
        preorder(node.right)

# Inorder: visit the node IN BETWEEN its children (ascending order on a BST)
def inorder(node):
    if node is not None:
        inorder(node.left)
        print(node.value)
        inorder(node.right)

# Postorder: visit the node AFTER both its children
def postorder(node):
    if node is not None:
        postorder(node.left)
        postorder(node.right)
        print(node.value)
// Node holds a value and up to two children
class Node {
    int value;
    Node left;
    Node right;
    Node(int value) {
        this.value = value;
    }
}

// Recursive insertion: compare, then repeat one level down
Node insert(Node node, int value) {
    if (node == null) {
        return new Node(value);
    }
    if (value < node.value) {
        node.left = insert(node.left, value);
    } else {
        node.right = insert(node.right, value);
    }
    return node;
}

// Recursive search: same left/right comparison, no insertion
boolean search(Node node, int value) {
    if (node == null) {
        return false;       // ran out of tree: not found
    }
    if (value == node.value) {
        return true;        // found it
    }
    if (value < node.value) {
        return search(node.left, value);
    }
    return search(node.right, value);
}

// Preorder: visit the node BEFORE its children
void preorder(Node node) {
    if (node != null) {
        System.out.println(node.value);
        preorder(node.left);
        preorder(node.right);
    }
}

// Inorder: visit the node IN BETWEEN its children (ascending order on a BST)
void inorder(Node node) {
    if (node != null) {
        inorder(node.left);
        System.out.println(node.value);
        inorder(node.right);
    }
}

// Postorder: visit the node AFTER both its children
void postorder(Node node) {
    if (node != null) {
        postorder(node.left);
        postorder(node.right);
        System.out.println(node.value);
    }
}

The guide's current generator only produces Python. Java is shown here as a reference for schools teaching that track; both follow the same left/right comparison.

This is B4's own guiding question in miniature, which ADT is most appropriate for a given situation: a sorted array and a BST solve the same problem, fast searching, but trade off very differently once insertion and deletion enter the picture.

Sorted array

Real example: a printed phone book. Fast to search (binary search), but inserting a new entry means reprinting the page: every entry after it has to shift along.

Advantage

  • Simple, no extra memory for pointers (just the values themselves)

Disadvantage

  • Inserting or deleting is slow (existing values must shift to keep the order)
Binary search tree

Real example: a digital contacts app. You can search it fast AND add or remove a contact instantly, no reprinting, no shifting, just a couple of pointers.

Advantage

  • Fast search, insertion and deletion all at once (no shifting; just repoint a left or right pointer)

Disadvantage

  • Extra memory for two pointers per node, and can become unbalanced (inserting already-sorted data in order produces a tree that degrades toward O(n) search)

Worked example

Inserting 64, 41, 88, 25, 52 in that order

64 arrives first and becomes the root. 41 is smaller than 64, so it becomes 64's left child. 88 is larger, so it becomes 64's right child. 25 is smaller than 64 AND smaller than 41, so it becomes 41's left child. 52 is smaller than 64 but larger than 41, so it becomes 41's right child.

Click a node to see its parent and children.

a leaf: no children

64 is the root: no parent. Its left child is 41, its right child is 88.

?

Why does 52 end up as 41's right child, not 64's? Insertion always starts at the root and walks down. 52 < 64, so it goes left to 41. Then 52 > 41, so from 41 it goes right. It never gets compared to 64 a second time; each step only compares against the node it has just arrived at.

Click through: inserting 77, then 95

Same tree, two new values landing on either side of 88. Use the arrows to step through each.

64
41
88
25
52

Inserting 77. Start at the root.

64
41
88
25
52

Now inserting 95 into the original tree. Start at the root.

Click through: searching for a value

Insert and delete get most of the attention, but searching is the operation a BST exists for. Same idea as the "higher or lower" guessing game from the top of this page: compare, then go left or right, every time.

64
41
88
25
52

Searching for 52. Start at the root, 64.

Click through: searching for a value that isn’t there

Just as important: knowing when to give up. A search has to terminate, not loop forever, when the value doesn’t exist.

64
41
88
25
52

Searching for 55. Start at the root, 64.

Traversal: visiting every node, in three different orders

Searching stops early once it finds (or rules out) a value. Traversal visits every single node. The three traversal orders (preorder, inorder, postorder) all use the exact same left/right structure; they only differ in when a node itself gets visited relative to its children.

!

The name tells you the timing. PREorder visits the node before its children (Node, Left, Right). POSTorder visits it after both children (Left, Right, Node). INorder visits it in between them (Left, Node, Right), which is also the only one of the three that happens to produce ascending order on a BST.

Click through: preorder (Node, Left, Right)

64
41
88
25
52

Preorder visits a node before its children. Start at 64: visit it immediately.

Click through: inorder (Left, Node, Right)

64
41
88
25
52

Inorder visits a node’s LEFT subtree first, before the node itself. From 50, go all the way left: to 30, then to 20.

Click through: postorder (Left, Right, Node)

64
41
88
25
52

Postorder visits a node LAST, after both its children. From 64, go all the way left first: to 41, then to 25.

Check your understanding

Which traversal of the tree above visits the nodes in ascending order: 25, 41, 52, 64, 88?

Common mistake

Markers reported this pattern for B4.1.4

RecurringMarkers reported two vocabulary-level mistakes on this statement specifically. Asked to describe the structure of a node, some candidates described the whole tree instead, a different and less precise question. Separately, markers noted candidates who did not know the definition of a leaf node, and others who identified the wrong node as the root when asked to sketch a tree from a list of insertions.

Preorder and postorder traversal are also regularly confused with each other, and with inorder.

✕

What a weak answer looks like: "A binary tree is a data structure made of nodes." True of the whole tree, but the question asked about one node's structure specifically.

✓

What it should say: "A node holds one value and up to two child references, a left child and a right child. A node with no children is a leaf." Answers at the node level, since that's what was asked.

Explainer

Deleting a node: three cases, and root is the hard one

Markers specifically noted candidates who could insert into and traverse a BST but could not delete a node, root deletion especially. There are three cases, and which one applies depends entirely on how many children the node being deleted has.

1

Deleting a leaf (like 25, 52 or 88 above): just remove it. Nothing else needs to change.

Click through: deleting a leaf, 52

The easy case first. Use the arrows to step through it.

64
41
88
25
52

Deleting 52. First check: does it have any children?

2

Deleting a node with one child: skip the deleted node entirely and wire its parent straight to its child, closing the one-node gap without disturbing anything below it.

Click through: deleting a middle node with one child, 88

Suppose 88 has already gained one child, 95 (exactly like the insertion above). Now delete 88 itself, the "one child" case.

64
41
88
25
52
95

Deleting 88. It's not the root and not a leaf: it has exactly one child, 95.

3

Deleting a node with two children (this is what makes deleting the root hard, the root usually has two children): the node can't just vanish without leaving the tree confused about what replaces it, so borrow a value that is guaranteed to slot in without breaking ordering, its inorder successor, found by walking to the right subtree once and then left as far as possible. Overwrite the deleted node with that value, then remove the successor from where it actually sat. Because a successor found this way can never have a left child of its own, removing it always collapses back into case 1 or case 2.

Click through: deleting the root, 64

The hardest case, worked through on the same tree. Use the arrows to step through it.

64
41
88
25
52

Deleting the root, 64. It has two children, 41 and 88, which is what makes root deletion the hard case.

Linking question

How this connects to the rest of the syllabus

The IB syllabus is deliberately woven together, not a stack of isolated topics. Here's how BSTs connect specifically to B1 (algorithms), worth thinking through, not just memorising: it's the kind of synthesis a Paper 1/2 question can genuinely ask for.

B1

Can a binary search tree play a role in the quicksort algorithm? (Both rely on the same core idea, compare against a pivot/root and recurse into the smaller side, a BST built from a list by repeated insertion produces the same partitioning quicksort does.)

Revision questions

Past-paper style. Write an answer, then reveal the marking points and mark yourself.

Question 1 · 1 mark

[1]Define the term leaf node.

Question 2 · 2 marks

[2]Sketch the binary search tree that results from inserting 15, 8, 23, 4 in that order.

Question 3 · 3 marks

[3]Explain why inserting a value into a binary search tree is naturally suited to a recursive algorithm.

Flashcards

Active recall for this statement's key terms.