← Linked lists CS Skill Tree

Removing repeats in 1 walk

The lesson behind it is Removing a node, every case.

The method removeRepeats walks a list once. Wherever 2 or more neighboring nodes hold equal values, it keeps the first of them, takes the rest out, and returns how many it took out. The list has a node in front of the first value that holds no value of its own (a dummy head).

Your readiness not started

Your own marking on this problem.

The contract

public int removeRepeats()

What the class already has:

No node holds null. 2 values are equal when equals returns true.

List beforeReturnsList aftersize after
empty0empty0
a0a1
a b c0a b c3
a a b1a b2
a b a0a b a3
a a b b b a3a b a3

The idea

To take a node out of a singly linked list, you change the next field of the node in front of it so that it skips the node (you unlink it). A node cannot see the node behind it, so the walk has to sit on the node in front. Here that node is always keep, the last node the method decided to keep.

  1. Put keep on head.next. Start removed at 0.
  2. Loop while keep is not null.
  3. While the node after keep holds a value equal to the value in keep, unlink it with keep.next = keep.next.next, subtract 1 from size, and add 1 to removed.
  4. keep stays put during step 3. The node that moves up behind it may be a repeat too.
  5. When the node after keep differs, or there is none, move keep 1 node forward.
  6. Return removed.

The first value never goes, because no value sits in front of it to repeat. So there is no front case. On an empty list, keep starts on null and the loop never runs.

The test in step 3 lives in a small private helper. Its null check comes first. && stops at the first part that is false (short-circuit evaluation), so node.next.data is read only when node.next exists.

public int removeRepeats() {
    int removed = 0;
    Node keep = head.next;
    while (keep != null) {
        while (repeatsNext(keep)) {
            keep.next = keep.next.next;
            size--;
            removed++;
        }
        keep = keep.next;
    }
    return removed;
}

private boolean repeatsNext(Node node) {
    return node.next != null && node.data.equals(node.next.data);
}

A trace on a a b b b a. Each row is 1 check of repeatsNext(keep).

Checkkeep is onNode after keepWhat changedListsizeremoved
11st aaunlink the 2nd aa b b b a51
21st abkeep moves to the 1st ba b b b a51
31st bbunlink the 2nd ba b b a42
41st bbunlink the 3rd ba b a33
51st bakeep moves to the last aa b a33
6last anonekeep moves to null; the loop ends; return 3a b a33

The cases

CaseList beforeReturnsPrinted afterWhat it checks
Emptyempty0List size is: 0the loop never runs
1 nodea0List size is: 1
a
the helper sees next is null and does not throw
No repeatsa b c0List size is: 3
a
b
c
nothing changes
Run at the fronta a b1List size is: 2
a
b
the first a stays
Run at the backa b b1List size is: 2
a
b
the unlink writes null into keep.next
All the samea a a a3List size is: 1
a
keep stays on the 1st a for the whole run
Equal but not neighborsa b a0List size is: 3
a
b
a
only neighbors count
Runs of 2 and 3a a b b b a3List size is: 3
a
b
a
2 runs in 1 walk

Drill

Work each one on paper before you tap.

Level-up check

1. Predict the output. The list holds x y y x x x front to back. removeRepeats() runs, then the return value and the list are printed. What comes out?

2. Spot the bug. In this version the inner while is an if. It runs on a a a b. What happens?

while (keep != null) {
    if (repeatsNext(keep)) {
        keep.next = keep.next.next;
        size--;
        removed++;
    }
    keep = keep.next;
}

3. Order of the checks. repeatsNext is called on the last node of the list, where node.next is null. Which return line gives false there without throwing?

4. An edge case. The list holds a b a b. What does removeRepeats() return, and what is printed?

4 questions. 3 right is a pass.

Write itThe inner loop

Write the inner loop of removeRepeats: the part that unlinks every repeat after keep and keeps size and removed correct.

Trace itp p p p, by hand

The list holds p p p p. For each check of repeatsNext(keep), write where keep is, what changed, size, and removed.

Your checklist

Tick these when they are true.

Could you write this cold?

Blank file, no notes, 4 hours on a clock, and only the Java API documentation open. Say yes only when that is true. You can take it back at any time.

Rules on the practice APE

You change only the lines between //startMethod and //endMethod, and you keep each method header as given.

A private helper method or field of your own between the markers is fine.

No new imports.

The official site's Basic Strategies page says: You may receive points for compiling code but you WON’T receive points for code that does not compile.

The deleteAllOccurrences problem in Practice exam 2 uses these moves.

Further reading