A playlist cursor holds the head of a singly linked track list. Product wants the order flipped — last song first — without allocating a second list of track objects. The intern version walked the list, pushed values onto an ArrayList, and built a new chain. The order was reversed. The nodes were not. Every listener that still held a node from the old list was looking at the wrong successor.

Reversing a singly linked list means rewiring next pointers so the old tail becomes the new head. The linked list post owns splice, dummy nodes, and why get(i) walks. Here we only care about three pointers and the one assignment that loses the rest of the chain if you get the order wrong.

The problem

Given the head of a singly linked list, reverse the list in place and return the new head. An empty list and a single node are legal; both are already reversed.

1 → 2 → 3 → 4 → 5 → null
5 → 4 → 3 → 2 → 1 → null

A node is the usual two fields:

final class ListNode {
    int val;
    ListNode next;

    ListNode(int val) {
        this.val = val;
    }
}

Note: In-place means the same node objects, new next links. Copying values into an array and building a fresh list passes a value-order test and fails any test that identity of nodes matters — or any follow-up that said O(1) extra space.

Three pointers, one assignment order

You cannot reverse current.next and still walk forward unless you saved what used to be next.

Names:

  • prev — the node that should follow current after the reverse. Starts null (old head becomes the new tail).
  • current — the node whose next you are about to rewire. Starts at head.
  • next — the old successor, saved before the rewire.

Each step:

next = current.next      // keep the rest of the list
current.next = prev      // reverse this link
prev = current           // this node is the new prev
current = next           // walk to the saved successor

Walk 1 → 2 → 3 → null:

start:  prev=null  current=1 → 2 → 3

step 1: 1.next = null,    prev=1, current=2     1 → null
step 2: 2.next = 1,       prev=2, current=3     2 → 1 → null
step 3: 3.next = 2,       prev=3, current=null  3 → 2 → 1 → null

return prev  (3)

When current is null, prev is the new head.

ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode current = head;
    while (current != null) {
        ListNode next = current.next;
        current.next = prev;
        prev = current;
        current = next;
    }
    return prev;
}

Time is O(n) — each node is visited once. Space is O(1) besides the three pointers. Recursion is the same rewire after the call returns; it costs O(n) stack and is the wrong default unless they ask for it.

Assigning current.next = prev before saving next drops the rest of the list on the floor. Say that out loud as you write the four lines. Interviewers listen for the save.

Empty list: current is already null, prev is null, you return null. One node: one iteration, next is null, that node points at null, you return it.

What interviewers usually poke next

  • Recursive version. Reverse the suffix, then head.next.next = head; head.next = null. Mention the stack bill.
  • Reverse a sublist m..n. Same three pointers after you walk to m, plus a handle on the node before m so you can splice the reversed run back.
  • Doubly linked. You also rewire prev. The idea is the same; the bug surface doubles. The layout post already has that variant.
  • Do it with a stack of nodes. Correct, O(n) extra space, and you should say why in-place is cheaper.

You are done with this problem when you can reverse 1 → 2 → 3 on a whiteboard without losing node 3, and you can name the empty-list return without a special case.