← Skill tree CS Skill Tree 0 CSCD211

Boundary Cases: Insert at Front or Back

Textbook: Liang

Try This First

You have a list [a, b, c] (size 3). You call add(0, "z"). Without looking at any code, describe what the list looks like after the call and identify which simpler operation handles it.

Reveal

The list becomes [z, a, b, c]. Because the index is 0, the method delegates directly to addFirst. No walk is needed.

What You Need To Walk In With

How It Works

The add(int i, T x) method has three regions:

  1. Boundary at the front (i == 0): delegate to addFirst.
  2. Boundary at the back (i == size): delegate to append.
  3. Middle (0 < i < size): walk to the predecessor at index i - 1, then splice in the new node.
public void add(int i, T x) {
    if (i < 0 || i > size) throw new IndexOutOfBoundsException();

    if (i == 0) {
        addFirst(x);          // updates head; no walk
        return;
    }
    if (i == size) {
        append(x);            // walks to tail (or uses tail ref); no special head logic
        return;
    }

    // Middle case: walk to predecessor
    Node<T> current = head;
    for (int j = 1; j < i; j++) {   // walk i-1 times: stop at index i-1
        current = current.next;
    }
    Node<T> newNode = new Node<>(x);
    newNode.next = current.next;      // point new node forward
    current.next = newNode;           // predecessor points to new node
    size++;
}

A pointer sketch for inserting "m" at index 2 in [a, b, c]:

Before:  head -> [a] -> [b] -> [c] -> null
                         ^
                    current (index 1, the predecessor)

After:   head -> [a] -> [b] -> [m] -> [c] -> null
                         |      ^      |
                  current.next  new   old current.next

The key detail: newNode.next is set before current.next is updated. Reversing those two lines loses the reference to [c].

Worked Example: Predict, Then Check

You have list = [a, b, c] (size 3). You call list.add(3, "d"). Which branch of the method runs, and what does the list look like after?

Reveal

Because i == size (both equal 3), the method delegates to append. The result is [a, b, c, d]. No predecessor walk runs; append handles the tail update.

A Common Mistake

A common error is skipping the i == 0 boundary check and letting the walk-to-predecessor logic run for every index. When i == 0, there is no predecessor: current starts at head, and the for-loop body (current = current.next) runs zero times, so current is head. Setting current.next = newNode makes the old head the second node, which is correct for the pointer chain, but head itself still points to the old first node. The new node is now at index 1, not index 0, and head is wrong. The fix is to check i == 0 first and call addFirst, which correctly updates head. (Source: S20.Lab5-LL_NDH)

Go Deeper (optional)

Both boundary delegations run in O(1) time if the list maintains a tail reference (as the dummy-head-and-tail variant does). In the no-dummy-head form covered here, append still walks to the tail in O(n). The middle case is O(n) because of the predecessor walk. Understanding which branch runs tells you the cost of any given add(i, x) call before you profile it.

Check Yourself

Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.

Check your understanding

For add(int i, T x) on a no-dummy-head singly linked list, which value of i triggers a call to addFirst?

Tier 1 · Liang 10e Ch 24

Why must the i == 0 boundary case be checked before the walk-to-predecessor loop?

Tier 2 · Liang 10e Ch 24

list = [a, b, c] (size 3). list.add(3, “d”) is called. Which branch runs and what is the result?

Tier 2 · Liang 10e Ch 24

In the middle-case code, the for-loop is: for (int j = 1; j < i; j++). If i == 2 and the list is [a, b, c], where does current stop?

Tier 3 · Liang 10e Ch 24

When writing: newNode.next = current.next; current.next = newNode; which statement must come first and why?

Tier 2 · Liang 10e Ch 24