Reverse Linked List in O(n) Time and O(1) Space using Java


Given the head of a singly linked list, reverse the list, and return the reversed list.

Example:

Input: head = [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]




Note:

Your algorithm should run in O(n) time and use O(1) extra space.


Problem Definition

The task is to reverse a singly linked list. Given the head of the list, we need to reverse the list and return the new head of the reversed list.

Input:

A singly linked list represented by its head node.

Output:

The head of the reversed singly linked list.

Constraints:

Example:

Input: head = [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]

Understanding the Problem

The core challenge is to reverse the links between the nodes of the linked list. This problem is significant in many applications such as reversing data structures, undo operations, and more. A common pitfall is losing track of the next node when reversing the links, which can lead to a broken list.

Approach

To solve this problem, we need to reverse the direction of the links between the nodes. We can achieve this by iterating through the list and adjusting the pointers.

Naive Solution

A naive approach might involve using additional space to store the nodes in a stack and then reconstructing the list. However, this would not meet the space complexity requirement of O(1).

Optimized Solution

The optimal solution involves using three pointers: prev, current, and next. We iterate through the list, reversing the links as we go.

Steps:

  1. Initialize three pointers: prev as null, current as head, and next as null.
  2. Iterate through the list:
    • Store the next node in next.
    • Reverse the current node's pointer to point to prev.
    • Move prev and current one step forward.
  3. Return prev as the new head of the reversed list.

Algorithm

Here is a step-by-step breakdown of the algorithm:

  1. Initialize prev to null and current to head.
  2. While current is not null:
    • Store current.next in next.
    • Set current.next to prev.
    • Move prev to current.
    • Move current to next.
  3. Return prev as the new head of the reversed list.

Code Implementation

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
public class Solution {
    public ListNode reverseList(ListNode head) {
        // Initialize previous node as null
        ListNode prev = null;
        // Initialize current node as head
        ListNode current = head;
        
        // Iterate through the list
        while (current != null) {
            // Store the next node
            ListNode next = current.next;
            // Reverse the current node's pointer
            current.next = prev;
            // Move pointers one position ahead
            prev = current;
            current = next;
        }
        
        // Return the new head of the reversed list
        return prev;
    }
}

Complexity Analysis

The time complexity of this algorithm is O(n) because we traverse the entire list once. The space complexity is O(1) because we only use a constant amount of extra space for the pointers.

Edge Cases

Consider the following edge cases:

Testing

To test the solution comprehensively, consider the following test cases:

1. Input: head = []
   Output: []

2. Input: head = [1]
   Output: [1]

3. Input: head = [1, 2, 3, 4, 5]
   Output: [5, 4, 3, 2, 1]

4. Input: head = [1, 2]
   Output: [2, 1]

Thinking and Problem-Solving Tips

When approaching such problems, consider the following tips:

Conclusion

Reversing a linked list is a fundamental problem that helps in understanding pointer manipulation and linked list operations. By practicing this problem, you can improve your problem-solving skills and gain a deeper understanding of linked lists.

Additional Resources

For further reading and practice, consider the following resources: