206 Reverse Linked List
Published:
Problem
Solution 1 - Iterative
Intuition
To flip a singly linked list in an iterative approach, we simply traverse down the linked list and flip the pointers to the next node in the list as we approach them. For each node, we keep track of the previous node, the current node, and the next node. The previous node will become the new node.next
. The current node is the node we are currently processing. And the next node is kept so we do not lose track of the original linked list.
Approach
- Initilize
prev
toNone
as we are starting on the first node and there is no previous node. - Initialize the current node to the given
head
for our starting point. - While the current node,
curr
, is notNone
, continue processing nodes. The process includes, trackingnext_node
, updating the current node’s next pointer,curr.next
to point to the previous node,prev
. Then updatingprev
to track the current node and updatingcurr
to track the next node we will visit, effectively sliding our process along by one node. - The
while
loop terminates when we reach the end and there are no more nodes left to process, we then returnprev
which holds the last node of the original list, or the first node of our reversed list.
Complexity
- Time complexity: $O(n)$. We iterate through each node performing constant time operations per iteration.
- Space complexity: $O(1)$. We do not utilize any other data structures and only use constant space for our variables.
Code
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
Solution 2 - Recursive
Intuition
The recursive solution is slightly harder to conceptualize even though the process is mostly the same. We traverse all nodes recursively instead of iteratively, but the process is mostly the same.
We start with our base case which is returning head
if either, 1. head
is None
or head.next
is None
. We then use result
which is how we use the recursive process to send up the last node back up the recursive chain so that after the list is reversed, it is the starting node of the reversed list to be returned.
The recursive calls continue until we reach our base case. This occurs when we reach the last node. We then return this value and it remains untouched to pass up the recursive chain. The rest of the process works in a similar fashion to the previous approach, but in a typical recursive fashion from the “bottom-up”. The first pointers which get flipped will be the second to last node’s and last node’s pointers, and then we work our way up the chain as result
continues to get passed and pointers continue to get flipped.
Approach
- Check our base cases. If
head
orhead.next
areNone
, returnhead
as we have either or 1 nodes and do not need to continue our recursive pattern. This also handles the edge case of when there are either 0 or 1 nodes. - Call the function recursively to return the last node,
result
. - Once recursive calls are finished, the processing will begin with the second to last node.
head.next.next
refers to the next value’s next pointer, so this is flipped to be the current node, and the current node’s next pointer,head.next
is removed. - We continue returning
result
which continues to point to the original list’s last node, and the reversed list’s first node. Once all recursive returns are called, this gets returned to the driver function as our final answer.
Complexity
- Time complexity: $O(n)$. We iterate through each node and perform constant time operations per iteration.
- Space complexity: $O(n)$. Recursive calls take stack space, and our recursive calls will use space linearly proportional to the length of the linked list.
Code
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
# Recursive
if head is None or head.next is None:
return head
result = self.reverseList(head.next)
head.next.next = head
head.next = None
return result