Ordered Linked Lists

IB Syllabus: B4.1.3 (HL) – Construct and apply linked lists: singly linked – insertion, deletion, traversal, search. This page focuses on maintaining sorted order.

This page is HL only.

Table of Contents

  1. Key Concepts
    1. Why Use an Ordered List?
    2. The Sorted Insertion Algorithm
    3. Reusing the Node Class
  2. Worked Examples
    1. Example 1: Sorted Insertion
    2. Example 2: Search with Early Termination
    3. Example 3: Remove from an Ordered List
  3. Quick Code Check
  4. Trace Exercise
  5. Code Completion
  6. Spot the Bug
  7. Output Prediction
  8. Practice Exercises
    1. Core
    2. Extension
    3. Challenge
  9. Connections

Key Concepts

An ordered linked list (also called a sorted linked list) is a singly linked list where nodes are always arranged in ascending (or descending) order based on their data values. Unlike a regular linked list where new items are added to the front or end, an ordered linked list inserts each new item at the correct position to maintain the sorted property.

Why Use an Ordered List?

Feature Unordered List Ordered List
Insertion O(1) at front O(n) – must find correct position
Search (item exists) O(n) – must check every node O(n) worst case, but can stop early
Search (item missing) O(n) – must check every node O(n) worst case, but stops as soon as a larger value is passed
Traversal output Arbitrary order Already sorted – no extra sorting step
Deletion O(n) to find O(n) to find, can stop early if passed

The key advantage: early termination. When searching for a value that does not exist, an unordered list must visit every node. An ordered list can stop as soon as it reaches a node with a larger value – the target cannot appear later in the list.

The Sorted Insertion Algorithm

To insert a value into an ordered list, you must handle four cases:

  1. Empty list – the new node becomes the head
  2. Insert before head – the new value is smaller than the current head
  3. Insert in the middle – traverse until you find the first node larger than the new value, insert before it
  4. Insert at the tail – the new value is larger than every existing node

Pointer order matters. When inserting in the middle, always set newNode.next to the successor before updating the predecessor’s next. Reversing this order loses the reference to the rest of the list.

Reusing the Node Class

This page uses the same Node class from the Linked Lists page:

public class Node {
    String data;
    Node next;

    public Node(String data) {
        this.data = data;
        this.next = null;
    }
}

Worked Examples

Example 1: Sorted Insertion

public class OrderedLinkedList {
    private Node head;

    public OrderedLinkedList() {
        head = null;
    }

    // Insert in sorted (ascending) order
    public void insertInOrder(String data) {
        Node newNode = new Node(data);

        // Case 1 & 2: empty list or insert before head
        if (head == null || data.compareTo(head.data) < 0) {
            newNode.next = head;
            head = newNode;
            return;
        }

        // Case 3 & 4: find the correct position
        Node current = head;
        while (current.next != null && current.next.data.compareTo(data) < 0) {
            current = current.next;
        }

        // Insert after current
        newNode.next = current.next;
        current.next = newNode;
    }

    public void printAll() {
        Node current = head;
        while (current != null) {
            System.out.print(current.data + " -> ");
            current = current.next;
        }
        System.out.println("null");
    }

    public int size() {
        int count = 0;
        Node current = head;
        while (current != null) {
            count++;
            current = current.next;
        }
        return count;
    }

    public static void main(String[] args) {
        OrderedLinkedList roster = new OrderedLinkedList();
        roster.insertInOrder("Diana");
        roster.insertInOrder("Ben");
        roster.insertInOrder("Eve");
        roster.insertInOrder("Aiko");
        roster.insertInOrder("Carlos");

        roster.printAll();
        System.out.println("Size: " + roster.size());
    }
}

Output:

Aiko -> Ben -> Carlos -> Diana -> Eve -> null
Size: 5

Despite being inserted in a random order, the names appear sorted because insertInOrder places each name at the correct alphabetical position. The compareTo method returns a negative value if the left string comes before the right alphabetically, zero if equal, and positive if after.

How each insertion works:

Inserted List before Position List after
Diana (empty) Head (case 1) Diana
Ben Diana Before head (case 2) Ben -> Diana
Eve Ben -> Diana After Diana (case 4) Ben -> Diana -> Eve
Aiko Ben -> Diana -> Eve Before head (case 2) Aiko -> Ben -> Diana -> Eve
Carlos Aiko -> Ben -> Diana -> Eve Between Ben and Diana (case 3) Aiko -> Ben -> Carlos -> Diana -> Eve

Example 2: Search with Early Termination

// Search that stops early when the target cannot exist
public boolean contains(String target) {
    Node current = head;
    while (current != null) {
        int comparison = current.data.compareTo(target);
        if (comparison == 0) {
            return true;     // found it
        }
        if (comparison > 0) {
            return false;    // passed where it would be -- not in list
        }
        current = current.next;
    }
    return false;            // reached end of list
}

In an unordered list, contains("Carl") would check every single node. In an ordered list, the method stops as soon as it reaches “Carlos” – because “Carlos” comes after “Carl” alphabetically, “Carl” cannot appear later in the list.

Example 3: Remove from an Ordered List

// Remove first occurrence, maintaining order
public boolean remove(String target) {
    if (head == null) {
        return false;
    }
    if (head.data.equals(target)) {
        head = head.next;
        return true;
    }
    Node current = head;
    while (current.next != null) {
        int comparison = current.next.data.compareTo(target);
        if (comparison == 0) {
            current.next = current.next.next;  // skip target node
            return true;
        }
        if (comparison > 0) {
            return false;    // passed where it would be
        }
        current = current.next;
    }
    return false;
}
public static void main(String[] args) {
    OrderedLinkedList list = new OrderedLinkedList();
    list.insertInOrder("Mango");
    list.insertInOrder("Apple");
    list.insertInOrder("Cherry");
    list.insertInOrder("Banana");

    System.out.println("Before:");
    list.printAll();

    list.remove("Cherry");
    System.out.println("After removing Cherry:");
    list.printAll();

    System.out.println("Contains Banana? " + list.contains("Banana"));
    System.out.println("Contains Cherry? " + list.contains("Cherry"));
}

Output:

Before:
Apple -> Banana -> Cherry -> Mango -> null
After removing Cherry:
Apple -> Banana -> Mango -> null
Contains Banana? true
Contains Cherry? false

Quick Code Check

Q1. What is the key difference between an ordered and an unordered linked list?

Q2. What does "Apple".compareTo("Cherry") return?

Q3. When inserting into an ordered list, what happens if the new value is smaller than the head?

Q4. Why can an ordered linked list search terminate early when the target is not found?

Q5. What is the time complexity of inserting into an ordered linked list?


Trace Exercise

Trace the state of the ordered linked list after each insertInOrder call.

OrderedLinkedList list = new OrderedLinkedList();
list.insertInOrder("Mango");
list.insertInOrder("Apple");
list.insertInOrder("Cherry");
list.insertInOrder("Banana");
list.insertInOrder("Peach");
After insertingInsertion caseFull list (head to tail)
Mango Mango -> null
Apple
Cherry
Banana
Peach

Code Completion

Complete the insertInOrder method to insert a value at the correct sorted position.

Fill in the blanks to find the insertion point and insert:

public void insertInOrder(String data) {
    Node newNode = new Node(data);
    if (head == null || data.(head.data) < 0) {
        newNode.next = head;
        head = ;
        return;
    }
    Node current = head;
    while (current. != null && current.next.data.compareTo(data) < 0) {
        current = current.next;
    }
    newNode.next = current.next;
    current. = newNode;
}

Complete the early-termination search method.

Fill in the blanks for the search that stops when it passes the target:

public boolean contains(String target) {
    Node current = head;
    while (current != null) {
        int comparison = current.data.compareTo();
        if (comparison == ) {
            return true;
        }
        if (comparison > 0) {
            return ;
        }
        current = current.next;
    }
    return false;
}

Spot the Bug

This insertInOrder method inserts "Apple" after "Mango" instead of before it:

1public void insertInOrder(String data) { 2 Node newNode = new Node(data); 3 if (head == null || data.compareTo(head.data) > 0) { 4 newNode.next = head; 5 head = newNode; 6 return; 7 }

Pick the correct fix for line 3:


This insertion loop puts values in the wrong position -- "Cherry" ends up before "Apple":

1// After head check passed: 2Node current = head; 3while (current.next != null && 4 current.next.data.compareTo(data) > 0) { 5 current = current.next; 6}

Pick the correct fix for line 4:


Output Prediction

What does this code print?

OrderedLinkedList nums = new OrderedLinkedList();
nums.insertInOrder("50");
nums.insertInOrder("20");
nums.insertInOrder("40");
nums.insertInOrder("10");
nums.insertInOrder("30");
nums.printAll();

Type the exact output:


What does this code print?

OrderedLinkedList fruits = new OrderedLinkedList();
fruits.insertInOrder("Cherry");
fruits.insertInOrder("Apple");
fruits.insertInOrder("Date");
fruits.insertInOrder("Banana");

System.out.println(fruits.contains("Coconut"));
System.out.println(fruits.contains("Cherry"));

Type the exact output:


Practice Exercises

Core

  1. Build a sorted roster – Create an OrderedLinkedList and insert 7 student names in random order. Print the list and verify all names appear in alphabetical order.
  2. Early termination count – Modify the contains method to also print how many nodes were visited before returning. Test it by searching for a value near the start, near the end, and one that does not exist. Compare the counts.
  3. Remove and verify – Insert 5 values in random order. Print the sorted list. Remove the middle value. Print again. Remove the head. Print again. Verify the list stays sorted after each removal.

Extension

  1. Insert with duplicates – Modify insertInOrder so that duplicate values are inserted immediately after the first occurrence of the same value. Test by inserting “Banana” twice and verify both appear in sequence.
  2. Merge two ordered lists – Write a method public static OrderedLinkedList merge(OrderedLinkedList a, OrderedLinkedList b) that creates a new ordered list containing all elements from both lists. The result must be sorted. Do not use insertInOrder for the merge – instead, walk both lists simultaneously, always picking the smaller head value.

Challenge

  1. Performance comparison – Write a program that inserts 1000 random strings into both an unordered SinglyLinkedList (using addToFront) and an OrderedLinkedList. Then search for 100 strings that do NOT exist in either list. Count the total number of node comparisons for each. Which list performs fewer comparisons for missing-value searches?
  2. Ordered list with integer keys – Rewrite the OrderedLinkedList to store int values instead of String. Adjust insertInOrder, contains, and remove to use integer comparison (<, >, ==) instead of compareTo. Test by inserting the numbers 50, 20, 40, 10, 30 and verify the sorted output.

Connections

  • Prerequisites: Linked Lists – Node class, basic insertion, deletion, and traversal
  • Related: Sorting Algorithms – ordered insertion achieves sorting as a side effect
  • Related: Binary Search Trees – BSTs provide O(log n) search on sorted data, improving on the O(n) of ordered lists
  • Next: Doubly Linked Lists – adding a prev pointer for bidirectional traversal

© EduCS.me — A resource hub for Computer Science education

This site uses Just the Docs, a documentation theme for Jekyll.