Problem overview
The function reverseList receives head, the first node of a singly linked list, and returns the head of the same list with every link turned around. Each *ListNode has a Val and a Next pointer. For [1, 2, 3, 4, 5] the result is [5, 4, 3, 2, 1]: the old last node becomes the head, and the old head becomes the tail, whose Next is nil.
The statement asks for O(n) time and O(1) extra space. So the function can't copy the values into a slice and rebuild the list. It has to rewire the nodes that already exist.
Approach: flip one link at a time
A singly linked list only points forward, so a node can't find the node before it. Reversing means making each node point at its predecessor instead of its successor. Walk the list from the front and, at each node, flip its single outgoing link.
There is one trap. The moment current.Next is overwritten, the rest of the list is no longer reachable from current. Take 1 -> 2 -> 3: if the link of node 1 is pointed backwards before anything is saved, nodes 2 and 3 are lost from node 1's point of view. So the walk keeps three pieces of information:
previous, the node that has already been reversed and sits just behindcurrent;current, the node whose link is flipped now;next, a saved copy ofcurrent.Next, taken before the link changes.
On [1, 2, 3] the state evolves like this:
- Start:
previousisnil,currentis node 1. Savenext= node 2, point node 1 atnil, then move on:previous= node 1,current= node 2. - Save
next= node 3, point node 2 at node 1, move on:previous= node 2,current= node 3. - Save
next=nil, point node 3 at node 2, move on:previous= node 3,current=nil.
The loop stops and previous, node 3, is the head of 3 -> 2 -> 1.
Step by step
- Declare
previousas a typed nil. The first node of the reversed list must end up pointing at nothing, sopreviousstarts asnil. Go can't infer a type from a barenil, soprevious := nilfails with the compile erroruse of untyped nil in assignment. The conversion(*ListNode)(nil)gives the variable the type*ListNode. - Declare
currentand set it tohead. This is the node being processed. - Loop while
current != nil. Go has nowhile; aforwith only a condition does the same job. - Save
current.Nextinnextbefore touching any link, so the rest of the list stays reachable. - Reverse the link:
current.Next = previous. For the first node this setsNexttonil, which is exactly what the new tail needs. - Advance
previoustocurrent. Do this before movingcurrent, otherwise the reference to the node just reversed is lost. - Advance
currentto the savednext. - Return
previous. When the loop ends,currentisnilandpreviousholds the last node that was reversed, the new head.
The solution in Go
func reverseList(head *ListNode) *ListNode {
previous := (*ListNode)(nil)
current := head
for current != nil {
next := current.Next
current.Next = previous
previous = current
current = next
}
return previous
}
Go details worth knowing:
- Pointers are copied, nodes are not.
current := headcopies the pointer, so movingcurrentnever changes the caller'sheadvariable. The nodes themselves are shared, which is why writing tocurrent.Nextchanges the caller's list. - Field access through a pointer uses a plain dot.
current.Nextworks on a*ListNodewithout any->or explicit dereference. nextis a fine variable name. Go has nonextkeyword, so the name doesn't collide with anything.- Unused variables are errors. Every variable declared with
:=here is used, which the compiler requires.
Complexity
Time: O(n). The loop visits each node once and does a fixed number of pointer assignments per node.
Space: O(1). Only the three pointers previous, current and next are stored. No new nodes are allocated; the existing ones are relinked.
Edge cases and common mistakes
- Empty list. With
head == nilthe loop never runs and the function returnsprevious, which isnil. No special case is needed. - One node. The loop runs once: the node's
Nextis set tonil(it already was) and the node is returned.[7]stays[7]. - Not saving
next. Writingcurrent.Next = previousand then trying to continue withcurrent.Nextwalks backwards into the part already reversed, so the rest of the list is never reached. - Wrong update order. Setting
current = nextbeforeprevious = currentmakespreviouspoint at the node that comes next instead of the one just reversed, so a link is lost. - Returning
head. After the reversal the original head is the tail, withNext == nil. Returning it gives a one-element list.
Testing
[1, 2, 3, 4, 5]gives[5, 4, 3, 2, 1].[7]gives[7].[](anilhead) gives[].
