The problem
You get the head of a singly linked list of integers. Return True if the values read the same forwards and backwards, otherwise False.
Input: 1 -> 2 -> 2 -> 1
Output: True
Input: 1 -> 2
Output: False1 2 2 1 reversed is still 1 2 2 1. 1 2 reversed is 2 1, which is different.
How to think about it
Start with brute force. Copy the values into a Python list and check vals == vals[::-1]. That is O(n) time and O(n) space, and it is a perfectly good first answer. The follow-up asks for O(1) extra space, and that is where it gets interesting.
Why it is hard on a linked list. A palindrome check compares the first with the last, the second with the second-last, and so on. In an array you would use two pointers moving inwards. A singly linked list cannot walk backwards, so the right-hand pointer has nowhere to go.
The key insight: make the back half walk backwards by reversing it. Find the middle with fast and slow pointers. Reverse the list from the middle onwards, in place. Now you have two forward-walking lists: the front half from head, and the reversed back half from the old tail. Walk them together and compare values. If every pair matches, it is a palindrome.
Which pattern is this? Fast & slow pointers to find the middle, combined with in-place list reversal. The tell is “compare a linked list with its own mirror in O(1) space”.
Approach
- Move
sone step andftwo steps untilfruns out.sis now the middle (the second middle for even lengths). - Reverse the list starting at
s, using the standardprev/nextswap.prevends on the old tail. - Walk
lfromheadandrfromprevtogether. Stop whenrruns out, because the reversed half is never longer than the front half. - If any pair of values differs, return
False. Otherwise returnTrue.
Solution
def is_palindrome_list(head):
s = f = head
while f and f.next: s = s.next; f = f.next.next # s = middle
prev = None
while s: s.next, prev, s = prev, s, s.next # reverse from the middle
l, r = head, prev # front half vs reversed back half
while r:
if l.val != r.val: return False
l = l.next; r = r.next
return True
# is_palindrome_list([1,2,2,1]) -> True ; is_palindrome_list([1,2]) -> FalseDry run
On 1 -> 2 -> 2 -> 1:
| Phase | State |
|---|---|
| find middle | s stops on the second 2 (index 2) |
reverse from s |
reversed back half is 1 -> 2 |
| compare 1 | l.val = 1, r.val = 1, match |
| compare 2 | l.val = 2, r.val = 2, match |
| end | r is None, return True |
On 1 -> 2, the middle is node 2, the reversed half is just 2, and the first comparison is 1 vs 2, so it returns False. On the odd list 1 2 3 2 1, the middle is 3 and the reversed half is 1 2 3; the middle node compares with itself.
Complexity
- Time: O(n). Finding the middle, reversing half, and comparing are each one linear pass.
- Space: O(1). Only a handful of pointers. The reversal reuses the existing nodes.
Edge cases and pitfalls
- Empty list or one node: always a palindrome. With
head = Nonethe comparison loop never runs and it returnsTrue. - The input is modified. After the call, the back half is reversed. If the caller still needs the list, reverse it back before returning. Mention this in an interview.
- Loop on
r, notl. The front half still links into the middle node, solcan run longer thanr. Stopping onrhandles odd and even lengths. - The tuple swap order matters.
s.next, prev, s = prev, s, s.nextevaluates the right side first, so it is safe. Rewriting it as three separate lines in the wrong order loses the rest of the list.
Related problems
- Middle of the Linked List (LeetCode 876): the first step of this solution on its own.
- Reverse Linked List (LeetCode 206): the reversal step on its own.
- Reorder List (LeetCode 143): the same middle-and-reverse set-up, followed by weaving instead of comparing.
- 1.Middle of the Linked List (LeetCode 876)
- 2.Linked List Cycle (LeetCode 141)
- 3.Happy Number (LeetCode 202)
- 4.Palindrome Linked List (LeetCode 234)
- 5.Intersection of Two Linked Lists (LeetCode 160)
- 6.Linked List Cycle II (LeetCode 142)
- 7.Find the Duplicate Number (LeetCode 287)
- 8.Remove Nth Node From End of List (LeetCode 19)
- 9.Reorder List (LeetCode 143)
- 10.Circular Array Loop (LeetCode 457)