Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 27

Reversing a Linked List

Recommended resources

Hello! In our previous lessons, we built both singly and doubly linked lists, focusing on their structure and basic operations like insertion and deletion. You've seen how manipulating next and prev pointers is the key to working with these data structures.

Today, we're diving into one of the most classic and fundamental linked list problems you'll encounter in technical interviews: reversing a linked list. Your goal is to learn how to reverse a linked list both iteratively and recursively. Mastering this will significantly strengthen your pointer manipulation skills and your ability to think recursively—both crucial for your goal of acing interviews at product-based companies.

We'll focus on reversing a singly linked list. This means we're not just changing the data inside the nodes; we are fundamentally changing the structure by redirecting the next pointers so the list runs in the opposite direction.

1. The Problem: Reversing the Links

Let's be clear about the task. If you are given the head of a list like this:
1 -> 2 -> 3 -> 4 -> NULL

Your function should modify the pointers to create this structure:
4 -> 3 -> 2 -> 1 -> NULL

And, importantly, your function must return the new head of the list, which in this case is the node containing 4.


2. The Iterative Approach: Juggling Pointers

The most common and space-efficient way to solve this problem is iteratively. The core idea is to traverse the list, and for each node we visit, we'll reverse its next pointer to point to the node that came before it.

To do this without losing track of the rest of the list, we need to manage three pointers at each step:

  1. previous: The node that came before the current one. It starts as null because nothing comes before the head.
  2. current: The node we are currently processing. It starts at the head.
  3. next: A temporary pointer to store the next node in the original list before we overwrite current.next.
Iterative Linked List Reversal Visualization
A visual of the original list and the target reversed list. The iterative method achieves this by changing the 'next' pointers one by one.

The algorithm proceeds as follows, looping until current becomes null:

  1. Save the next node: next = current.next;
  2. Reverse the pointer: current.next = previous;
  3. Move pointers forward: previous = current; and current = next;

Once the loop finishes, previous will be pointing to the original tail of the list, which is our new head.

To see this logic animated step-by-step, the following video provides an exceptionally clear walkthrough. Pay close attention to how the three pointers (previous, current, and front/next) interact in each iteration.

L9. Reverse a LinkedList | Iterative and Recursive

This video from 'take U forward' provides a detailed dry run of the iterative solution. It's a great way to visualize the pointer manipulation.

Watch the segment from 04:58 to 13:20, which covers the optimal iterative algorithm. The visualization will help solidify your understanding of the three-pointer technique.

Java Implementation (Iterative)

Here is a clean Java implementation of the iterative approach.

Reverse a Linked List: With Code Examples and Animation

The article 'Reverse a Linked List' provides a concise explanation and a clean Java implementation for the iterative method.

Focus on the 'Iterative Approach' section. Review the logic and the Java code provided under 'Code (Java)'.

The core logic from the article looks like this:

// Assuming a ListNode class is defined:
// class ListNode {
//     int val;
//     ListNode next;
//     ListNode(int x) { val = x; }
// }

public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode current = head;
    ListNode next = null;

    while (current != null) {
        // Store the next node before we change the pointer
        next = current.next;
        
        // Reverse the current node's pointer
        current.next = prev;
        
        // Move prev and current one step forward
        prev = current;
        current = next;
    }
    
    // At the end, prev will be the new head
    return prev;
}
  • Time Complexity: O(N) because we visit each node exactly once.
  • Space Complexity: O(1) because we only use a few extra pointers, regardless of the list's size. This is the optimal solution and the one you should aim for in an interview.
Test your understanding!

Given the list 10 -> 20 -> 30 -> NULL, trace the values of prev, current, and next at the end of each while loop iteration. What is the final returned value?

Show answer

Initial State: prev = null, current = 10, next = null

After Iteration 1:

  • next becomes 20.
  • current.next (10's next) becomes null.
  • prev becomes 10.
  • current becomes 20.
    State: prev = 10, current = 20

After Iteration 2:

  • next becomes 30.
  • current.next (20's next) becomes 10.
  • prev becomes 20.
  • current becomes 30.
    State: prev = 20, current = 30

After Iteration 3:

  • next becomes null.
  • current.next (30's next) becomes 20.
  • prev becomes 30.
  • current becomes null.
    State: prev = 30, current = null

The loop terminates because current is null. The function returns prev, which is the node with value 30.


3. The Recursive Approach: Divide and Conquer

The recursive solution is elegant but can be harder to grasp initially. It's a great exercise for strengthening your recursion skills. The main idea is to think of the problem in terms of a smaller, already solved subproblem.

Logic:

  1. Base Case: If the list is empty (head == null) or has only one node (head.next == null), it's already reversed. So, we just return the head.
  2. Recursive Step:
    • Assume we can magically reverse the rest of the list (from head.next onwards). We call reverseList(head.next).
    • This recursive call returns the newHead of the reversed sub-list.
    • Now, we need to attach the original head to the end of this reversed sub-list.
      • head.next still points to the original next node, which is now the tail of the reversed sub-list.
      • So, we set head.next.next = head; to make that tail node point back to our current head.
      • Finally, we must break the original forward link to avoid a cycle: head.next = null;.
    • We return the newHead that we received from the recursive call, as it is the head of the entire reversed list.
Visualizing Recursive Linked List Reversal
Visualizing the result of recursive reversal. The function repeatedly calls itself on a smaller part of the list until it reaches the end, then stitches the nodes back together in reverse order.

This process can feel like magic. The following video does a phenomenal job of demystifying it by walking through the call stack and showing how the pointers are re-linked as the recursion unwinds.

L9. Reverse a LinkedList | Iterative and Recursive

This section from 'take U forward' explains the recursive solution. The dry run showing the call stack is essential for understanding how the list gets reversed.

Watch the segment from 14:42 to 30:41. Focus on the base case and how the pointers head.next.next and head.next are manipulated after the recursive call returns.

Java Implementation (Recursive)

Here is the corresponding Java code for the recursive method.

Reverse a Linked List: With Code Examples and Animation

The article 'Reverse a Linked List' also presents the recursive approach with a clear explanation of the base case and the pointer reversals.

Read the 'Recursive Approach' section and its accompanying Java code. Try to connect the code back to the call stack explanation you saw in the video.

The recursive function is remarkably concise:

public ListNode reverseList(ListNode head) {
    // Base case: empty list or list with only one node
    if (head == null || head.next == null) {
        return head;
    }
    
    // Recursively reverse the rest of the list
    ListNode newHead = reverseList(head.next);
    
    // Reverse the pointers for the current node
    head.next.next = head;
    head.next = null;
    
    // Return the new head (which is passed up from the base case)
    return newHead;
}
  • Time Complexity: O(N), as each node is visited once during the recursive calls.
  • Space Complexity: O(N), due to the depth of the recursion call stack. In the worst case, a list of size N will lead to N nested function calls before the base case is reached.

Conclusion

Congratulations! You've just learned two powerful ways to reverse a linked list, a problem that is a staple of coding interviews.

Key Takeaways:

  • Iterative Reversal: Uses three pointers (previous, current, next) to reverse the links in a single pass. It's the most efficient solution with O(N) time and O(1) space complexity. This is the standard expected answer in most interviews.
  • Recursive Reversal: Solves the problem by breaking it down: reverse the tail of the list, then attach the head to the end. It's an elegant solution with O(N) time and O(N) space complexity (due to the call stack).

Understanding both approaches demonstrates a deep command of pointer manipulation and recursion.

Preview of the next lesson:
We will continue exploring classic linked list problems. Next, we will tackle another interview favorite: detecting a cycle in a linked list. You'll learn a clever and efficient pointer-based technique known as Floyd's Tortoise and Hare algorithm.