Facebook Pixel

206. Reverse Linked List

Problem Description

You are given the head of a singly linked list. Your task is to reverse the entire linked list and return the head of the reversed list.

In a singly linked list, each node contains a value and a pointer to the next node. The reversal means that all the arrows pointing from one node to the next should be flipped in the opposite direction.

For example:

  • If the original list is: 1 -> 2 -> 3 -> 4 -> 5
  • The reversed list should be: 5 -> 4 -> 3 -> 2 -> 1

The solution uses a head insertion technique with a dummy node. Here's how it works:

  1. Create a dummy node that will serve as a temporary head
  2. Traverse through the original list one node at a time
  3. For each node visited:
    • Save the reference to the next node in the original list
    • Insert the current node right after the dummy node (at the beginning of the new list)
    • Move to the saved next node
  4. After processing all nodes, dummy.next points to the new head of the reversed list

The key insight is that by always inserting nodes right after the dummy node, we naturally build the list in reverse order. Each new node becomes the new first node of the reversed portion.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to reverse a linked list, we essentially want to flip all the arrows between nodes. The challenge is that in a singly linked list, we can only traverse forward, and once we change a node's next pointer, we lose access to the rest of the original list.

Think about how you would reverse a stack of papers: you'd take papers from the top of the original stack and place them one by one onto a new stack. The first paper from the original becomes the last in the new stack, and the last paper from the original becomes the first in the new stack.

We can apply a similar principle here using a "head insertion" strategy. Imagine we have a new empty list (represented by a dummy node), and we want to build the reversed list by always adding nodes at the beginning of this new list.

As we traverse the original list from head to tail:

  • The first node we encounter (originally at position 1) gets inserted at the front of our new list
  • The second node (originally at position 2) also gets inserted at the front, pushing the first node to position 2
  • This continues, with each subsequent node pushing all previous nodes one position back

By consistently inserting at the head position, we naturally achieve the reversal. The last node we process (the tail of the original list) ends up at the front of our new list, becoming the new head.

The dummy node technique is particularly elegant here because it eliminates edge cases. We don't need special handling for an empty list or the first node - the dummy node provides a consistent anchor point where dummy.next always points to the current head of our reversed portion.

Solution Approach

The implementation uses the head insertion method with a dummy node to reverse the linked list. Let's walk through the algorithm step by step:

Initial Setup:

  • Create a dummy node with dummy = ListNode(). This node serves as a placeholder before the head of our reversed list
  • Set curr = head to start traversing from the beginning of the original list

Main Reversal Loop: The while curr: loop continues until we've processed all nodes:

  1. Save the next node: next = curr.next

    • We must save the reference to the next node before modifying any pointers, otherwise we'd lose access to the rest of the original list
  2. Insert current node at the head: curr.next = dummy.next

    • Point the current node's next to whatever is currently after the dummy node (initially None, then the previously inserted nodes)
  3. Update dummy to point to current: dummy.next = curr

    • Make the dummy node point to the current node, effectively placing it at the beginning of our reversed list
  4. Move to the next node: curr = next

    • Advance to the saved next node to continue the traversal

Return the result: After the loop completes, dummy.next points to the head of the fully reversed list.

Example walkthrough with list 1 -> 2 -> 3:

  • Initially: dummy -> None, curr = 1
  • Iteration 1: Insert node 1 → dummy -> 1 -> None, curr = 2
  • Iteration 2: Insert node 2 → dummy -> 2 -> 1 -> None, curr = 3
  • Iteration 3: Insert node 3 → dummy -> 3 -> 2 -> 1 -> None, curr = None
  • Return dummy.next which points to node 3 (the new head)

The time complexity is O(n) where n is the number of nodes, as we visit each node exactly once. The space complexity is O(1) since we only use a constant amount of extra space regardless of the input size.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through reversing the linked list 1 -> 2 -> 3 using the head insertion approach:

Initial State:

  • Original list: 1 -> 2 -> 3 -> None
  • Create dummy node: dummy -> None
  • Set curr = 1 (pointing to first node)

Iteration 1: Processing Node 1

  • Save next: next = 2 (save reference to node 2)
  • Insert node 1 after dummy:
    • curr.next = dummy.next → Node 1 now points to None
    • dummy.next = curr → Dummy now points to node 1
  • Result: dummy -> 1 -> None
  • Move forward: curr = 2

Iteration 2: Processing Node 2

  • Save next: next = 3 (save reference to node 3)
  • Insert node 2 after dummy:
    • curr.next = dummy.next → Node 2 now points to node 1
    • dummy.next = curr → Dummy now points to node 2
  • Result: dummy -> 2 -> 1 -> None
  • Move forward: curr = 3

Iteration 3: Processing Node 3

  • Save next: next = None (node 3 has no next)
  • Insert node 3 after dummy:
    • curr.next = dummy.next → Node 3 now points to node 2
    • dummy.next = curr → Dummy now points to node 3
  • Result: dummy -> 3 -> 2 -> 1 -> None
  • Move forward: curr = None

Final Result:

  • Loop ends since curr = None
  • Return dummy.next which points to node 3
  • Reversed list: 3 -> 2 -> 1 -> None

Notice how each node gets inserted at the front (right after dummy), naturally building the list in reverse order. The first node processed (1) ends up at the tail, while the last node processed (3) becomes the new head.

Solution Implementation

1# Definition for singly-linked list.
2# class ListNode:
3#     def __init__(self, val=0, next=None):
4#         self.val = val
5#         self.next = next
6
7class Solution:
8    def reverseList(self, head: ListNode) -> ListNode:
9        """
10        Reverses a singly-linked list iteratively.
11      
12        Args:
13            head: The head node of the linked list to reverse.
14          
15        Returns:
16            The new head of the reversed linked list.
17        """
18        # Create a dummy node to simplify insertion at the beginning
19        dummy_node = ListNode()
20      
21        # Pointer to traverse the original list
22        current = head
23      
24        # Iterate through each node in the original list
25        while current:
26            # Store the next node before we modify current's next pointer
27            next_node = current.next
28          
29            # Insert current node at the beginning of the reversed list
30            # Point current's next to whatever dummy is pointing to
31            current.next = dummy_node.next
32          
33            # Make dummy point to the current node (new head of reversed list)
34            dummy_node.next = current
35          
36            # Move to the next node in the original list
37            current = next_node
38      
39        # Return the head of the reversed list (dummy's next)
40        return dummy_node.next
41
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 *     int val;
5 *     ListNode next;
6 *     ListNode() {}
7 *     ListNode(int val) { this.val = val; }
8 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12    /**
13     * Reverses a singly linked list iteratively.
14     * 
15     * @param head The head of the original linked list
16     * @return The head of the reversed linked list
17     */
18    public ListNode reverseList(ListNode head) {
19        // Create a dummy node to simplify the reversal process
20        // The dummy node will point to the reversed list
21        ListNode dummyNode = new ListNode();
22      
23        // Initialize current pointer to traverse the original list
24        ListNode current = head;
25      
26        // Iterate through each node in the original list
27        while (current != null) {
28            // Store the next node before breaking the link
29            ListNode nextNode = current.next;
30          
31            // Insert current node at the beginning of the reversed list
32            // Point current node to whatever dummy is pointing to
33            current.next = dummyNode.next;
34          
35            // Make dummy point to the current node (insert at front)
36            dummyNode.next = current;
37          
38            // Move to the next node in the original list
39            current = nextNode;
40        }
41      
42        // Return the head of the reversed list (node after dummy)
43        return dummyNode.next;
44    }
45}
46
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 *     int val;
5 *     ListNode *next;
6 *     ListNode() : val(0), next(nullptr) {}
7 *     ListNode(int x) : val(x), next(nullptr) {}
8 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13    /**
14     * Reverses a singly linked list iteratively.
15     * Uses a dummy node to simplify the reversal process.
16     * 
17     * @param head The head of the original linked list
18     * @return The head of the reversed linked list
19     */
20    ListNode* reverseList(ListNode* head) {
21        // Create a dummy node to act as the new list's predecessor
22        ListNode* dummyNode = new ListNode();
23      
24        // Pointer to traverse the original list
25        ListNode* currentNode = head;
26      
27        // Iterate through each node in the original list
28        while (currentNode != nullptr) {
29            // Store the next node before we break the link
30            ListNode* nextNode = currentNode->next;
31          
32            // Insert current node at the beginning of the new list
33            // Point current node to what dummy is pointing to
34            currentNode->next = dummyNode->next;
35            // Make dummy point to the current node
36            dummyNode->next = currentNode;
37          
38            // Move to the next node in the original list
39            currentNode = nextNode;
40        }
41      
42        // Return the head of the reversed list (node after dummy)
43        return dummyNode->next;
44    }
45};
46
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 *     val: number
5 *     next: ListNode | null
6 *     constructor(val?: number, next?: ListNode | null) {
7 *         this.val = (val===undefined ? 0 : val)
8 *         this.next = (next===undefined ? null : next)
9 *     }
10 * }
11 */
12
13/**
14 * Reverses a singly linked list iteratively
15 * @param head - The head node of the linked list to reverse
16 * @returns The new head of the reversed linked list
17 */
18function reverseList(head: ListNode | null): ListNode | null {
19    // Handle empty list case
20    if (head === null) {
21        return head;
22    }
23  
24    // Initialize pointers for reversal
25    let previousNode: ListNode | null = null;  // Tracks the previous node in reversed list
26    let currentNode: ListNode | null = head;    // Tracks the current node being processed
27  
28    // Iterate through the list and reverse the links
29    while (currentNode !== null) {
30        // Store the next node before we change the current node's next pointer
31        const nextNode: ListNode | null = currentNode.next;
32      
33        // Reverse the current node's pointer to point to the previous node
34        currentNode.next = previousNode;
35      
36        // Move pointers forward for next iteration
37        previousNode = currentNode;
38        currentNode = nextNode;
39    }
40  
41    // Return the new head (which is the last processed node)
42    return previousNode;
43}
44

Time and Space Complexity

The time complexity is O(n), where n is the length of the linked list. This is because the algorithm uses a single while loop that traverses through each node in the linked list exactly once. Each operation inside the loop (pointer reassignment) takes constant time O(1), and since we visit all n nodes, the total time complexity is O(n).

The space complexity is O(1). The algorithm only uses a constant amount of extra space regardless of the input size. It creates one dummy node and uses two pointer variables (curr and next) to track positions during the reversal process. These variables do not scale with the size of the input linked list, hence the constant space complexity.

Common Pitfalls

1. Losing the reference to the remaining list

One of the most frequent mistakes when reversing a linked list is forgetting to save the reference to the next node before modifying pointers. This leads to losing access to the rest of the original list.

Incorrect approach:

while current:
    current.next = dummy_node.next  # Oops! We just lost the original current.next
    dummy_node.next = current
    current = current.next  # This now points to dummy_node.next, not the original next node!

This creates an infinite loop or incorrect traversal because current.next has been modified before we saved its original value.

Correct approach:

while current:
    next_node = current.next  # Save the reference FIRST
    current.next = dummy_node.next
    dummy_node.next = current
    current = next_node  # Use the saved reference

2. Confusing the order of pointer updates

Another common mistake is updating the pointers in the wrong order, which can create cycles or break the list structure.

Incorrect approach:

while current:
    next_node = current.next
    dummy_node.next = current  # Setting this first
    current.next = dummy_node.next  # Now current.next points to itself!
    current = next_node

This creates a self-loop because dummy_node.next is already pointing to current when we set current.next = dummy_node.next.

Correct approach:

while current:
    next_node = current.next
    current.next = dummy_node.next  # First: point current to the existing reversed portion
    dummy_node.next = current  # Then: make dummy point to current
    current = next_node

3. Not handling edge cases properly

Forgetting to handle empty lists or single-node lists can cause issues, though the dummy node approach naturally handles these cases well.

Potential issue without proper initialization:

def reverseList(self, head: ListNode) -> ListNode:
    if not head:  # Unnecessary check with dummy node approach
        return None
    dummy_node = ListNode()
    # ... rest of code

Better approach (as in the solution): The dummy node approach elegantly handles all edge cases:

  • Empty list: The while loop never executes, returns dummy_node.next which is None
  • Single node: Loop executes once, correctly returns that single node
  • Multiple nodes: Works as expected

The beauty of the dummy node approach is that it treats all cases uniformly without special handling.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More