title: Reverse Linked List slug: reverse-linked-list difficulty: easy leetcode_id: 206 leetcode_url: https://leetcode.com/problems/reverse-linked-list/ categories: - linked-lists - recursion patterns: - linkedlist-reversal description: | Given the `head` of a singly linked list, reverse the list, and return *the reversed list*. constraints: | - The number of nodes in the list is in the range `[0, 5000]` - `-5000 <= Node.val <= 5000` examples: - input: "head = [1,2,3,4,5]" output: "[5,4,3,2,1]" explanation: "The list 1→2→3→4→5 becomes 5→4→3→2→1." - input: "head = [1,2]" output: "[2,1]" explanation: "The list 1→2 becomes 2→1." - input: "head = []" output: "[]" explanation: "An empty list remains empty when reversed." explanation: intuition: | Imagine a chain of paper clips linked together, each pointing to the next. To reverse the chain, you need to make each clip point to the *previous* one instead of the next one. Think of it like this: for each node, we're changing the direction of its arrow. Originally `A → B → C`, we want `A ← B ← C`. The challenge is that when we change a node's `next` pointer to point backwards, we lose our reference to move forward! The solution is to use **three pointers**: - `prev`: The node we're pointing back to - `curr`: The node we're currently processing - `next_node`: Saved reference to the next node (so we don't lose it when we reverse the link) After processing all nodes, the original tail becomes the new head. Since `curr` ends up as `None` (past the last node), `prev` points to the last processed node — our new head. approach: | We solve this using an **Iterative Three-Pointer Approach**: **Step 1: Initialise pointers** - `prev = None`: The new tail will point to `None` - `curr = head`: Start at the beginning of the list - We don't initialise `next_node` yet — we'll set it inside the loop   **Step 2: Iterate through the list** - While `curr` is not `None`: - **Save the next node**: `next_node = curr.next` (before we lose it!) - **Reverse the link**: `curr.next = prev` (point backwards) - **Move prev forward**: `prev = curr` - **Move curr forward**: `curr = next_node`   **Step 3: Return the new head** - When the loop ends, `curr` is `None` (we've passed the last node) - `prev` points to what was the last node — now the first node - Return `prev` as the new head   This processes each node exactly once with constant extra space. common_pitfalls: - title: Losing the Reference to the Next Node description: | If you reverse the link *before* saving the next node, you can't continue traversing! ```python # WRONG: We lose access to the rest of the list curr.next = prev curr = curr.next # This now points backwards! ``` Always save `next_node = curr.next` **before** modifying `curr.next`. wrong_approach: "curr.next = prev; curr = curr.next" correct_approach: "next_node = curr.next; curr.next = prev; curr = next_node" - title: Returning curr Instead of prev description: | After the loop, `curr` is `None` — we've moved past the end of the list. The actual new head is `prev`, which points to the last node we processed. Think about it: in the final iteration, `curr` points to the last node, we process it, then `curr = next_node` makes `curr = None`. wrong_approach: "return curr" correct_approach: "return prev" - title: Not Handling Empty or Single-Node Lists description: | The algorithm handles these edge cases naturally: - **Empty list**: `head = None`, loop never executes, return `prev = None` - **Single node**: Loop runs once, `prev` becomes the single node, which is returned No special-case code is needed, but it's good to trace through these cases to verify. wrong_approach: "Adding unnecessary if-checks for edge cases" correct_approach: "Trust the algorithm — it handles empty and single-node lists" key_takeaways: - "**Fundamental operation**: Reversing a linked list is used in many problems (palindrome check, reverse in groups, etc.)" - "**Three-pointer technique**: `prev`, `curr`, `next` is a common pattern for in-place linked list manipulation" - "**Save before modifying**: When changing pointers, always save references you'll need later" - "**Both iterative and recursive work**: Iterative is O(1) space, recursive is O(n) but more elegant — know both!" time_complexity: "O(n). We visit each of the n nodes exactly once, performing O(1) work at each." space_complexity: "O(1). We only use three pointer variables, regardless of list length." solutions: - approach_name: Iterative is_optimal: true code: | class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverse_list(head: ListNode | None) -> ListNode | None: prev = None # Will become the new tail curr = head # Start at the head while curr: # Save next node before we overwrite the link next_node = curr.next # Reverse the link: point backwards curr.next = prev # Move pointers forward prev = curr curr = next_node # prev is now the new head (curr is None) return prev explanation: | **Time Complexity:** O(n) — Single pass through all nodes. **Space Complexity:** O(1) — Only three pointers used. We iterate through the list once, reversing each link as we go. The key is saving the next node before modifying `curr.next`, then advancing both `prev` and `curr` forward. - approach_name: Recursive is_optimal: false code: | def reverse_list(head: ListNode | None) -> ListNode | None: # Base case: empty list or single node if not head or not head.next: return head # Recursively reverse the rest of the list new_head = reverse_list(head.next) # head.next is now the tail of the reversed sublist # Make it point back to head head.next.next = head # head is now the tail, so point it to None head.next = None # Return the new head (unchanged through recursion) return new_head explanation: | **Time Complexity:** O(n) — Each node processed once. **Space Complexity:** O(n) — Recursion stack depth equals list length. The recursive approach reverses the rest of the list first, then fixes the link for the current node. `head.next.next = head` makes the next node point back to us, and `head.next = None` makes us the new tail. The new head is propagated back through all recursive calls.