Palindrome Linked List asks a simple question — does the list read the same forwards and backwards? — but doing it in O(1) extra space forces a neat trick: find the middle, reverse the back half in place, then walk inward from both ends.
Problem. Given the head of a singly linked list, return True if the list is a palindrome and
False otherwise. A palindrome reads the same forwards and backwards.
Example: 1 -> 2 -> 2 -> 1 → True. 1 -> 2 -> 3 → False (forwards is 1,2,3; backwards is 3,2,1).
The slow way first
The obvious idea: copy every value into an array, then check whether the array equals its reverse. That works and is easy to write, but it uses O(n) extra space for the array.
The question to ask: can I compare the front and back without copying anything? In an array I could use two pointers from both ends — but a singly linked list has no way to walk backwards. So the move is to make the back half walkable in reverse, in place.
The idea: split, reverse, compare
Do it in three phases, all with constant extra space:
- Find the middle with a slow/fast pointer. fast moves two steps for every one of slow, so when fast falls off the end, slow sits at the middle.
- Reverse the second half in place by flipping each
nextpointer backwards (the classic three-pointer reverse). - Walk both halves inward: one pointer from the original head, one from the new head of the reversed back half. Compare values as they meet in the middle.
The key insight: once the back half points backwards, the two halves can be scanned toward each other in a single pass, with no array and no recursion stack.
Walk through it
Step through the animation with 1 -> 2 -> 2 -> 1. In phase 1, slow advances one node and fast two, so fast lands on the last node and slow marks the middle. In phase 2, the link 2 -> 1 is flipped to 1 -> 2, so the back half now starts at the last node. In phase 3, left starts at the head and right at that reversed head; we compare 1 == 1, then 2 == 2, the pointers meet, and the verdict is Palindrome.
Pseudocode
slow = fast = head
while fast and fast.next: # find the middle
slow = slow.next
fast = fast.next.next
prev = None # reverse the second half
curr = slow.next
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
left = head # compare the two halves
right = prev
while right:
if left.val != right.val:
return False
left = left.next
right = right.next
return TrueThe Python solution
def is_palindrome(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
prev = None
curr = slow.next
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
left = head
right = prev
while right:
if left.val != right.val:
return False
left = left.next
right = right.next
return True- Lines 2-5 are the slow/fast walk. Because
fastcovers two nodes per step, it reaches the end in half the time, leavingslowat the middle. - Lines 6-12 are the in-place reverse of the second half — the same save / flip / advance loop used to reverse any linked list.
prevends up at the new head of the reversed back half. - We compare against
right(the reversed half) until it runs out. WalkingrightuntilNonenaturally stops at the middle, so for an odd-length list the leftover middle node is simply ignored. - The first mismatch returns
Falseearly; if the loop finishes, every pair matched and we returnTrue.
Complexity
| Case | Time | Notes |
|---|---|---|
| Copy to array, compare | O(n) (moderate) | simple but O(n) extra space |
| Split + reverse + compare | O(n) (moderate) | three linear passes, in place |
O(1) (fast)Both approaches are O(n) time, but this one uses only O(1) extra space — no array, no recursion stack. The cost is a little more pointer bookkeeping, which is exactly what interviewers want to see.
When this pattern shows up
Two linked-list moves combine here, and both are worth memorizing on their own: the slow/fast pointer to find the middle (or detect a cycle), and the three-pointer reverse to flip a list in place. Many list problems — reorder list, palindrome check, find the middle — are just these two primitives stitched together.
This rewires the list as a side effect: the second half ends up reversed. If the caller still needs the original list intact, reverse the back half a second time before returning. Also handle the trivial cases up front — an empty list or a single node is a palindrome.
Practice
After phase 1 on 1 -> 2 -> 2 -> 1, where does slow stop, and which node becomes the head of the reversed second half?
1. Why use a slow and a fast pointer in phase 1?
2. What is the main advantage of this approach over copying values into an array?
3. How does the comparison loop handle an odd-length list?
4. What side effect does this algorithm leave on the input list?