Problem overview
The method Reverse receives the head of a singly linked list and returns the head of the reversed list. For [1, 2, 3, 4, 5] it returns [5, 4, 3, 2, 1]. The nodes are ListNode objects with an int val and a ListNode next reference.
The lesson asks for O(n) time and O(1) extra space. That rules out copying the values into an array or a List<int> and rebuilding the list. The nodes themselves have to be rewired.
Approach: reverse each link in place
Reversing a list means that every link a -> b becomes b -> a, and that the old last node becomes the head. The idea is to walk the list once from left to right and flip one link per step.
The danger is losing the way forward. Suppose that while standing on node 2 of 1 -> 2 -> 3 you write node.next = prev right away. Node 2 now points back at node 1, and nothing refers to node 3 any more, so the walk cannot continue. The fix is to save the next node before changing the link, and to remember the previous node so that the current one has something to point back to. The same idea works for a list of any length, because each step only needs its immediate neighbors.
That gives three pointers during the loop:
prev: the node before the current one, which the current node will point back to.node: the node whose link is being flipped.nextNode: the saved rest of the list.
Tracing 1 -> 2 -> 3:
- Setup:
previs node 1,nodeis node 2, and node 1'snextis cut tonull. The list is nownull <- 1and2 -> 3. - First pass: save node 3, make 2 point to 1, move
prevto 2 andnodeto 3. Nownull <- 1 <- 2and3remains. - Second pass: save
null, make 3 point to 2, moveprevto 3 andnodetonull. - The loop stops.
previs node 3, the head of3 -> 2 -> 1.
Step by step
- Declare
prevas aListNodeand start it athead. The original head will become the last node. - Declare
nodeand start it athead.next, the second node, because the head is handled separately. - Cut the head's link with
head.next = null. If the head kept pointing at the second node, the reversed list would end in a cycle between the first two nodes. - Loop while
node != null. - Save the rest in
nextNode = node.nextbefore changing any link. - Flip the link:
node.next = prev. - Move
prevforward tonode. - Move
nodeforward tonextNode. The order of steps 7 and 8 matters:prevtakes the current node first, and only then doesnodemove on. - Return
prev. Whennodebecomesnull,prevholds the last node processed, which is the new head.
The solution in C#
class Solution {
public ListNode Reverse(ListNode head) {
ListNode prev = head;
ListNode node = head.next;
head.next = null;
while (node != null) {
ListNode nextNode = node.next;
node.next = prev;
prev = node;
node = nextNode;
}
return prev;
}
}
C# details worth knowing
- References, not copies.
ListNodeis a class, soprev = nodecopies a reference, and both variables now refer to the same node. Nothing is duplicated, which is why the method needs no extra memory. - Declaring inside the loop.
ListNode nextNodeis declared on each pass, and its scope ends with the loop body. It is only a reference, so there is no cost. - The argument is modified. The method rewires the caller's nodes. After the call, the original
headvariable still refers to the first node, which is now the last, and itsnextisnull. - Null handling. Reading
head.nextwhenheadisnullthrowsNullReferenceException. This solution assumes the list has at least one node. An empty list would need a guard such asif (head == null) return null;before the first line.
Complexity
The loop visits each node once and does a constant amount of work, so the time is O(n). Only three references are kept beyond the list itself, so the extra space is O(1). There is no recursion, so the call stack does not grow with the list length.
Edge cases and common mistakes
- A single node.
nodestarts asnull, the loop never runs, andprevis the head itself, so the result is the same one-node list. - Two nodes.
1 -> 2becomes2 -> 1. The loop runs once. - Not saving
node.next. If you assignnode.next = prevfirst, the rest of the list is lost and the loop ends early. - Forgetting
head.next = null. The old head would still point at the second node, and the new tail would point back into the list, which creates a cycle. - Returning
head. After reversing, the original head is the tail. The new head isprev. - Updating in the wrong order. Setting
node = nextNodebeforeprev = nodewould moveprevto the wrong node.
Testing
Results from running the solution:
[1, 2, 3, 4, 5]returns[5, 4, 3, 2, 1].[1, 2]returns[2, 1].[1]returns[1].
