Problem overview
The method reverse(_ head: ListNode?) -> ListNode? receives the head of a singly linked list and returns the head of the same list in reverse order. For 1 -> 2 -> 3 -> 4 -> 5 it returns 5 -> 4 -> 3 -> 2 -> 1. The target is O(n) time and O(1) extra space, so the method can't copy the values into an array and build a new list. It has to rewire the links of the nodes it was given.
Each node is a ListNode with an Int field val and an optional field next, of type ListNode?, which is nil at the end of the list.
Approach: flip each link as you walk
Reversing a list means that every arrow a -> b becomes b -> a, and that the old tail becomes the new head. The two-pointer trick used to reverse an array doesn't apply, because a singly linked list can't be walked backwards.
The straightforward attempt is to visit a node and set node.next.next = node. It breaks as soon as it runs: after the first link is flipped, the original path forward from the next node is gone, so the walk loses the rest of the list. The remedy is to remember the next node before changing anything.
The walk therefore uses three references:
prev, the node just behind the current one, which the current node's link must point to after the flip;node, the node whose link is being flipped;nextNode, saved fromnode.nextbefore the flip, so the walk can continue.
On 1 -> 2 -> 3, the starting state is prev = 1 and node = 2, with the head's link cut. Flipping 2 gives 2 -> 1; then prev = 2 and node = 3. Flipping 3 gives 3 -> 2 -> 1; then node is nil and prev holds 3, the new head.
Step by step
- Declare
prevwithvarand start it athead. It tracks the node before the current one. - Declare
nodewithvarand start it at the second node,head!.next. The first node is handled separately. The!force-unwrapshead, which is aListNode?, so the code assumes the list is not empty. - Cut the head's link with
head!.next = nil. The head becomes the last node of the reversed list. If it kept pointing at the second node, the reversed list would end in a cycle. - Loop with
while node != nil. - Save the rest in
let nextNode = node!.next. Without this, rewritingnode!.nextloses every node afternode. Swift doesn't treatnodeas non-nilinside the loop, so it needs the!. - Flip the link:
node!.next = prev. - Advance
prev:prev = node. - Advance
node:node = nextNode. The order of these two assignments matters: doingnode = nextNodefirst would lose the current node before it is stored inprev. - Return
prev. When the loop ends,nodeisnilandprevis the old last node, which is the new head.
The solution in Swift
class Solution {
func reverse(_ head: ListNode?) -> ListNode? {
var prev = head
var node = head!.next
head!.next = nil
while node != nil {
let nextNode = node!.next
node!.next = prev
prev = node
node = nextNode
}
return prev
}
}
Swift details worth a closer look:
- Classes are references.
ListNodeis a class, soprev,nodeandheadhold references to node objects. Settingnode!.next = prevchanges the node itself, and every other reference to that node sees the change. A struct would be copied instead, and the code wouldn't work. - Optionals and
!.headand eachnextare optional. The!operator unwraps one and crashes at run time if it isnil. In the loop,node!is safe because the condition just checked it. node != nil. Comparing an optional withnilworks even whenListNodeis notEquatable, because the check asks only whether a value is present, not whether two nodes are equal.- Type inference.
var prev = headhas the typeListNode?, andvar node = head!.nexthas the same type, so assigningprev = nodeandnode = nextNodetype-checks. - Memory. Swift frees a node when no reference to it remains. Reversing in place never drops a node, because each one stays reachable through
prev,nodeor the new list.
Complexity
Time: O(n). The loop runs once per node after the first, and each pass does a fixed number of assignments.
Space: O(1). Three references (prev, node, nextNode) are used, whatever the length of the list. No new nodes are created.
Edge cases and common mistakes
- One node. For
7,nodestarts asnil, the loop never runs, and the method returns the same node. Itsnextwas alreadynil. - Two nodes.
1 -> 2becomes2 -> 1after one pass. - Empty list. This version force-unwraps
headinhead!.next, so an empty list (nil) crashes. The lesson's tests use lists with at least one node. A safer opening isguard let head = head else { return nil }. - Forgetting to cut the head's link. The original head would still point at the second node, which now points back at it, a cycle that makes later traversals loop forever.
- Not saving
nextNode. Afternode!.next = prev, the way forward is lost, so the loop ends early or never finishes. - Updating the pointers in the wrong order.
prevmust take the current node beforenodemoves on. - Returning
head. The old head is now the last node, and returning it gives a list of one element. - After the call. The old
headvariable still refers to the same node, which is now the tail. Use the returned value as the new head.
Testing
[1, 2, 3, 4, 5]gives[5, 4, 3, 2, 1].[1, 2]gives[2, 1].[7]gives[7].[1, 2, 3]gives[3, 2, 1], and the old head node (value 1) is now the tail, so walking from it gives just[1].

