Session 4 · Reversing a Linked List#
The big idea#
A line of children, each holding the hand of the child in front. You want the line to face the other way. Each child must let go of the child in front and grab the hand of the child behind.
You do it one child at a time, from the front. Before a child lets go, you must remember who is next in line, otherwise the rest of the line is lost.
That is the whole pattern: flip one link, remember the next node, step forward.
In plain words
Walk through the list with three names (prev, curr, next). At each node, point it backwards
instead of forwards. No new list is created, so memory stays O(1).
Reach for it when:
you must reverse a linked list, or part of it (between positions
leftandright)you need to reverse in groups of k
a problem needs the second half reversed (palindrome check, reorder list)
The picture#
Step by step#
Reverse 1 → 2 → 3 → null. We keep prev = None and curr = head.
step |
|
save |
flip |
move |
|---|---|---|---|---|
1 |
1 |
2 |
|
|
2 |
2 |
3 |
|
|
3 |
3 |
None |
|
|
curr is None, so we stop. prev is the new head: 3 → 2 → 1 → null.
class Node:
def __init__(self, val, next=None):
self.val, self.next = val, next
def reverse(head):
prev, curr = None, head
while curr:
nxt = curr.next # 1. remember the rest
curr.next = prev # 2. flip the link
prev, curr = curr, nxt # 3. step forward
return prev
def to_list(n):
out = []
while n:
out.append(n.val); n = n.next
return out
print(to_list(reverse(Node(1, Node(2, Node(3))))))
Output:
[3, 2, 1]
Time O(n), space O(1).
Reversing only a part#
Reverse the nodes from position left to right.
Walk to the node just before
left(call itbefore). A dummy node in front of the head avoids special cases whenleft = 1.Reverse
right - left + 1nodes with the same three-line loop.Reconnect the three pieces:
before→ reversed part → the rest.
def reverse_between(head, left, right):
dummy = Node(0, head)
before = dummy
for _ in range(left - 1):
before = before.next
curr = before.next
for _ in range(right - left): # move one node to the front, repeatedly
nxt = curr.next
curr.next = nxt.next
nxt.next = before.next
before.next = nxt
return dummy.next
head = Node(1, Node(2, Node(3, Node(4, Node(5)))))
print(to_list(reverse_between(head, 2, 4)))
Output:
[1, 4, 3, 2, 5]
Using it inside other problems#
Palindrome linked list in O(1) space: find the middle (session 3), reverse the second half, compare it with the first half, optionally reverse it back.
def is_palindrome(head):
slow = fast = head
while fast and fast.next:
slow, fast = slow.next, fast.next.next
second = reverse(slow)
first = head
while second:
if first.val != second.val:
return False
first, second = first.next, second.next
return True
Common mistakes#
Warning
Flipping before saving
next. Thencurr.nextis gone and the rest of the list is lost.Forgetting to return
prev(notcurr, which isNoneat the end).Not reconnecting the pieces in sub-list reversal, which leaves the list broken or cyclic.
Skipping the dummy node and fighting special cases for
left = 1.
Practice#
Problem |
Level |
What to notice |
|---|---|---|
Easy |
the basic loop |
|
Medium |
sub-list + reconnect |
|
Hard |
repeat the sub-list reversal |
|
Easy |
middle + reverse half |
|
Medium |
middle, reverse, merge |