Splitting a list into 2 lists
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.
The contract
public LinkedList splitAlternate()
What the class already has:
private class Nodewith the fieldsdataandnext, and the constructorNode(Object data, Node next). This method never calls that constructor. Every node it uses already exists.private Node headis the first real node, ornull.private int sizecounts the nodes. The method keeps it right on both lists.addFirst(Object data)puts a value at the front and adds 1 tosize.toString()printsList size is: N, then 1 value per line from front to back.
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.
| Input | Result | Size after |
|---|---|---|
| Empty list | Returns an empty list, never null. This list stays empty. | this 0, returned 0 |
| 1 node | Returns an empty list. This list keeps its node. | this 1, returned 0 |
| n nodes, n of 2 or more | Nodes 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
- Make the new list first, empty, and call it
other. Every path returnsother, so the method never returnsnull. - Put
keeponthis.head.keepalways sits on a node that stays. - Set
tailtonull. It will point at the last node ofother(a tail pointer), so each moved node can go on the end. - Loop while
keepandkeep.nextare both notnull. The node afterkeepis the node that moves. - Save that node in
moving. Then pointkeep.nextpast it. Now this list skipsmoving. - Set
moving.nexttonull. This cuts the link frommovingback into this list (to sever it). - Attach
movingtoother. It becomesother.headwhenotheris empty, and goes aftertailotherwise. Thentailmoves tomoving. - Add 1 to
other.sizeand take 1 fromthis.size. - Step
keeptokeep.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.
| Turn | keep on | moving | This list | Returned list | this.size | other.size |
|---|---|---|---|---|---|---|
| start | a | none | a b c d e | empty | 5 | 0 |
| 1 | a | b | a c d e | b | 4 | 1 |
| 2 | c | d | a c e | b d | 3 | 2 |
| end | e | none | a c e | b d | 3 | 2 |
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.
| Case | List before | This list after | Returned list |
|---|---|---|---|
| Empty | empty | size 0 | size 0 |
| 1 node | a | size 1: a | size 0 |
| 2 nodes | a b | size 1: a | size 1: b |
| 3 nodes | a b c | size 2: a c | size 1: b |
| 5 nodes, odd length | a b c d e | size 3: a c e | size 2: b d |
| 6 nodes, even length | a b c d e f | size 3: a c e | size 3: b d f |
| Equal values | x x x x | size 2: x x | size 2: x x |
| Second call on a c e | a c e | size 2: a e | size 1: c |
- Empty and 1 node. The loop test fails on the first check, so nothing moves and
othercomes back empty. Noiffor these cases is needed. - Odd length. The last node stays. Without the sever,
dstill points ate, and the returned list printseas well. - Even length. The last node moves. Then
keepsteps tonull, which is why the loop test checkskeepbeforekeep.next. - Equal values. The method moves by position. It never compares values.
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?
The nodes at positions 1 and 3 move, and those hold 10 and 20. The second choice is what stays behind, which is what you get when you count positions from 1. The third choice comes from putting each moved node at the front of the new list, which reverses the order. The fourth choice comes from never adding 1 to other.size.
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;
}
Each moved node still points at the node after it in the old list. The next move fixes that for b, because tail.next = moving overwrites it. Nothing fixes it for d, the last node moved, so d.next is still e. Now e sits in both lists, and the returned list prints 3 values under a size of 2. On a b c d e f the bug hides, because the last moved node is f, whose next was already null. Test an odd length.
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?
Save the node before you relink past it. The first choice relinks first, so the list reads a c and moving holds c, and nothing refers to b any more. The third choice points b at itself, so the list reads a b b b and never ends. The fourth choice does not compile, because moving is used before it is declared.
4. An edge case. A list holds only a. What does list.splitAlternate() return?
keep starts on a, and a.next is null, so the loop body never runs. The method returns other, which was made empty on the first line. A caller that prints the result would crash on null, which is why other is made before anything else. The node at position 0 stays, so a stays in this list.
Write it
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.
if (tail == null) {
other.head = moving;
} else {
tail.next = moving;
}
tail = moving;
The first moved node has nothing in front of it, so it becomes other.head. Every later node goes after tail. The last line runs on both branches, so tail is always on the newest node.
Write the while line of splitAlternate. Then say which case needs each half of the test.
while (keep != null && keep.next != null)
keep != null handles the empty list, where keep starts at null. It also handles an even length, where keep steps to null after the last move. keep.next != null handles a 1-node list and an odd length, where keep stops on a last node that stays. The order matters, because && stops at the first false half, so keep.next is never read through null.
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.
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, atailand 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.