Problem overview
The method reverse receives the head of a singly linked list and returns the head of the reversed list. For the list 1 -> 2 -> 3 -> 4 -> 5 the result is 5 -> 4 -> 3 -> 2 -> 1. The statement asks for O(n) time and O(1) extra space, so the method can't copy the values into a second structure. It has to change the links of the existing nodes.
Approach: flip each link, but save the rest first
Reversing the list means making every node point to the node that used to be before it. The old head becomes the last node, and the old last node becomes the new head.
The first idea is to walk the list and, for each node, rewrite node.next so it points backwards. That has a trap. The moment node.next is overwritten, the reference to the following node is gone, and the rest of the list can no longer be reached. On the list 1 -> 2 -> 3, flipping the link of node 1 gives 1 <- 2 and leaves node 3 unreachable from the loop.
The fix is to remember the next node before changing anything, and to carry the node behind the current one so the flipped link has somewhere to point. The loop therefore uses three references:
prev, the node behind the current one (the part of the list already reversed ends here);node, the node whose link is being flipped;nextNode, a saved copy of the node ahead.
Each iteration flips one link and moves all of the references one step forward. Every node is visited once and no new nodes are made, which gives O(n) time and O(1) space.
On 1 -> 2 -> 3, start with prev = 1 and node = 2, and cut the old head so it ends the list: 1 -> null. Iteration one saves node 3, points 2 back at 1, then moves: prev = 2, node = 3. Now the reversed part is 2 -> 1. Iteration two saves null, points 3 at 2, then prev = 3 and node = null. The loop ends, and prev holds 3 -> 2 -> 1.
Step by step
- Declare
prevwithvarand initialize it withhead. It is the node before the current one. - Declare
nodewithvarand start it at the second node,head.next. The head itself is handled in the next step. - Cut the head's link:
head.next = null. The head becomes the tail of the reversed list. If it kept pointing at the second node, the reversed list would end in a cycle. - Loop while
node != null. - Save the rest in
nextNode, declared withvalbecause it is never reassigned. Without it, writing tonode.nextwould lose every node afternode. - Flip the link: set
node.nexttoprev. - Advance
prev: the current node becomes the previous one. - Advance
node: set it to the savednextNode. Updateprevfirst, thennode; the reverse order would lose the current node. - Return
prev. When the loop ends,nodeisnullandprevis the last node that was processed, which is the head of the reversed list.
The solution in Scala
class Solution {
def reverse(head: ListNode): ListNode = {
var prev = head
var node = head.next
head.next = null
while (node != null) {
val nextNode = node.next
node.next = prev
prev = node
node = nextNode
}
prev
}
}
The Scala details worth a closer look:
varversusval.prevandnodechange on every iteration, so they arevar.nextNodeis created fresh in each iteration and never reassigned, so it is aval. The type of each is inferred asListNode.- Mutable fields.
node.next = prevonly compiles becausenextis declared as avarfield ofListNode. The method modifies the caller's nodes, so the original list is destroyed. After reversing 1 -> 2 -> 3, the old head (the node 1) is the last node, and followingnextfrom it givesnull. - The last expression is the result. The method ends with
prev, and Scala returns it without areturnkeyword. nullchecks.node != nullcompares references, and!=in Scala also works onnullwithout throwing.
Complexity
Time: O(n). The loop runs once for each node after the first, and each iteration does a fixed number of assignments.
Space: O(1). The method uses three references however long the list is. A recursive solution, or one that copies values into an array, would use O(n) extra space.
Edge cases and common mistakes
- A single node.
nodeisnullfrom the start, the loop doesn't run, and the method returns the same node, whosenextisnull. - Two nodes. 1 -> 2 becomes 2 -> 1 after one iteration.
- An empty list. This solution reads
head.nextfirst, so anullhead throws aNullPointerException. The lesson's tests use lists with at least one node. To support the empty list, returnnullat the top whenhead == null. - Forgetting
head.next = null. The old head would keep pointing at the second node, which now points back to it, and the reversed list would contain a cycle. Printing or traversing it would never finish. - Flipping before saving. If you write
node.next = prevbefore savingnextNode, the following nodes are unreachable. - Updating in the wrong order. Setting
node = nextNodebeforeprev = nodemovesprevto the wrong node. - Returning
head. After the loop the old head is the tail. The new head isprev.
Testing
- 1 -> 2 -> 3 -> 4 -> 5 gives 5 -> 4 -> 3 -> 2 -> 1.
- 1 -> 2 gives 2 -> 1.
- A single node 1 gives 1.
- Reversing twice returns a list in the original order, as long as you use the head returned the first time.

