← Linked lists CS Skill Tree

Splitting a list into 2 lists

The lesson behind it is Making a sublist.

The method splitAlternate moves every second node out of a list and into a new list, then returns the new list. The list has no empty node in front of its first value (a sentinel), so head is the first real node, or null when the list is empty.

Your readiness not started

Your own marking on this problem.

The contract

public LinkedList splitAlternate()

What the class already has:

Positions count from 0 (a 0 based position). The first node is at position 0 and the second node is at position 1. The nodes at the odd positions 1, 3, 5 and so on are the ones that move.

InputResultSize after
Empty listReturns an empty list, never null. This list stays empty.this 0, returned 0
1 nodeReturns an empty list. This list keeps its node.this 1, returned 0
n nodes, n of 2 or moreNodes at positions 1, 3, 5 and so on move to the returned list, in the same order. Nodes at 0, 2, 4 and so on stay.returned n / 2, this n - n / 2 (Java integer division)

Moving nodes without building any

  1. Make the new list first, empty, and call it other. Every path returns other, so the method never returns null.
  2. Put keep on this.head. keep always sits on a node that stays.
  3. Set tail to null. It will point at the last node of other (a tail pointer), so each moved node can go on the end.
  4. Loop while keep and keep.next are both not null. The node after keep is the node that moves.
  5. Save that node in moving. Then point keep.next past it. Now this list skips moving.
  6. Set moving.next to null. This cuts the link from moving back into this list (to sever it).
  7. Attach moving to other. It becomes other.head when other is empty, and goes after tail otherwise. Then tail moves to moving.
  8. Add 1 to other.size and take 1 from this.size.
  9. Step keep to keep.next. Since step 5 relinked it, that is the next node that stays.
public LinkedList splitAlternate() {
    LinkedList other = new LinkedList();
    Node keep = this.head;
    Node tail = null;
    while (keep != null && keep.next != null) {
        Node moving = keep.next;
        keep.next = moving.next;
        moving.next = null;
        if (tail == null) {
            other.head = moving;
        } else {
            tail.next = moving;
        }
        tail = moving;
        other.size++;
        this.size--;
        keep = keep.next;
    }
    return other;
}

The trace below runs the method once on a list that holds a b c d e, front to back.

Turnkeep onmovingThis listReturned listthis.sizeother.size
startanonea b c d eempty50
1aba c d eb41
2cda c eb d32
endenonea c eb d32

At the end keep is on e, and e.next is null, so the loop stops. The 2 sizes add up to 5, the size before the call.

The cases

Each list is written front to back. Each case calls splitAlternate once and prints both lists.

CaseList beforeThis list afterReturned list
Emptyemptysize 0size 0
1 nodeasize 1: asize 0
2 nodesa bsize 1: asize 1: b
3 nodesa b csize 2: a csize 1: b
5 nodes, odd lengtha b c d esize 3: a c esize 2: b d
6 nodes, even lengtha b c d e fsize 3: a c esize 3: b d f
Equal valuesx x x xsize 2: x xsize 2: x x
Second call on a c ea c esize 2: a esize 1: c

The printed output of 4 of the cases, line for line. The label above each list comes from the test driver, not from toString.

Empty, this list:
List size is: 0
Empty, returned list:
List size is: 0

1 node, this list:
List size is: 1
a
1 node, returned list:
List size is: 0

5 nodes, this list:
List size is: 3
a
c
e
5 nodes, returned list:
List size is: 2
b
d

6 nodes, this list:
List size is: 3
a
c
e
6 nodes, returned list:
List size is: 3
b
d
f

Drill

Level-up check

1. Predict the output. A list holds 5 10 15 20 25, front to back. The code runs LinkedList moved = list.splitAlternate(); and then prints moved. What comes out?

2. Spot the bug. This version leaves out the line moving.next = null; and is otherwise the same. It runs on a b c d e. What does the returned list print?

Node moving = keep.next;
keep.next = moving.next;
if (tail == null) {
    other.head = moving;
} else {
    tail.next = moving;
}

3. Pointer order. The list holds a b c and keep is on a. Which 2 lines, in this order, take b out of the list and still hold on to it?

4. An edge case. A list holds only a. What does list.splitAlternate() return?

4 questions. 3 right is a pass.

Write it

Write itAttach a node to the end of the new list

moving has just been taken out of this list, and its next is null. tail is the last node of other, or null when other is empty. Write the lines that put moving on the end of other and leave tail on it.

Write itThe loop test

Write the while line of splitAlternate. Then say which case needs each half of the test.

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 cutList problem in Practice exams 1 and 2 uses these moves.

Further reading
  • LinkedList (Java SE 19 & JDK 19). The standard library's own linked list and what each of its methods promises.
  • Linked list (Wikipedia). Splitting and joining chains of nodes, and why both cost so little on this structure.
  • SLList: A Singly-Linked List, from Pat Morin's Open Data Structures. A full Java implementation that keeps a head, a tail and a size.
  • Bags, Queues, and Stacks, from Sedgewick and Wayne's Algorithms at Princeton. Linked lists built from scratch, including a queue that adds at the end through a pointer to the last node.