Given the head
of a singly linked list, reverse the list, and return the reversed list.
Example 1:
Input: head = [1,2,3,4,5] Output: [5,4,3,2,1]
Example 2:
Input: head = [1,2] Output: [2,1]
Example 3:
Input: head = [] Output: []
Constraints:
[0, 5000]
.-5000 <= Node.val <= 5000
Follow up: A linked list can be reversed either iteratively or recursively. Could you implement both?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Imagine a train where each car is linked to the next. The brute force way to reverse this train involves physically rearranging all the cars. We'll meticulously copy the train's data to create a reversed version.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_linked_list_brute_force(head):
node_values = []
current_node = head
while current_node:
node_values.append(current_node.val)
current_node = current_node.next
reversed_head = None
# Iterate backwards to create the reversed list
for value in reversed(node_values):
# Create a new node with the reversed value.
new_node = ListNode(value)
# Update head to create the new list.
new_node.next = reversed_head
reversed_head = new_node
return reversed_head
To reverse a linked list, you essentially want to change the direction each element points. Instead of creating a new list, the optimal solution rearranges the existing one in place, one step at a time. This avoids extra storage and is faster.
Here's how the algorithm would work step-by-step:
def reverse_linked_list(head):
previous_node = None
current_node = head
# We iterate until current_node becomes None
while(current_node is not None):
# Store the next node before reversing
next_node = current_node.next
# Reverse the current node's pointer
# This is the core reversal step
current_node.next = previous_node
# Move pointers one position ahead
previous_node = current_node
current_node = next_node
# previous_node is now the head of reversed list
return previous_node
Case | How to Handle |
---|---|
Null or empty list | Return null immediately; there's nothing to reverse. |
List with only one node | Return the original head as it's already reversed. |
List with two nodes | Standard iterative or recursive reversal should handle this correctly by swapping the head and head.next, updating pointers. |
Long linked list | The iterative approach scales linearly and won't cause stack overflow problems, as opposed to recursion. |
List containing duplicate values | The values do not affect the reversal logic, so duplicates are irrelevant; reversal will still work as expected. |
List containing negative values | Negative values within the linked list should have no impact on the pointer manipulation during reversal. |
Memory limitations for extremely large lists | The iterative approach uses constant extra space, mitigating memory issues related to list size, unlike a naive recursive implementation which could cause stack overflow. |
List with loop (corrupted data) | The standard reversal algorithm might lead to an infinite loop; a check for loop using Floyd's cycle detection algorithm could be incorporated before reversing or the reversal algorithm may need to be modified to handle this scenario |