234. Palindrome Linked List
On LeetCode ->Reformulated question¶
Given the head of a singly linked list, return whether its values read the same forward and backward.
Example:
Key trick¶
Use slow/fast pointers to find the middle, reverse the second half in place, then compare both halves.
Trap¶
- Forgetting odd length handling:
- skip the middle node before comparing.
- Using extra array memory:
- works, but misses the \(O(1)\) extra space follow-up.
- Mutating nodes by adding
prevon a singly linked list:- this changes the data structure instead of solving the problem.
- Crashing on small lists:
- especially when accessing
right.nextwithout checkingright.
- especially when accessing
Why this question is interesting¶
It combines three classic linked-list skills in one problem:
- find middle
- reverse list
- compare halves
It is a clean test of pointer manipulation and edge-case control.
Solve the problem with idiomatic Python¶
from typing import Optional
from utils import ListNode
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
# 0 or 1 node is always a palindrome.
if head is None or head.next is None:
return True
# Find the middle.
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# For odd length, skip the middle node.
if fast:
slow = slow.next
# Reverse the second half in place.
prev = None
curr = slow
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
# Compare first half with reversed second half.
left = head
right = prev
while right:
if left.val != right.val:
return False
left = left.next
right = right.next
return True
Pytest test¶
import pytest
from utils import list_build
@pytest.mark.parametrize(
("values", "expected"),
[
([1], True),
([1, 1], True),
([1, 2], False),
([1, 2, 1], True),
([1, 2, 2, 1], True),
([1, 2, 3, 2, 1], True),
([1, 2, 3, 4, 1], False),
([1, 0, 1], True),
([1, 2, 3, 2, 2], False),
],
)
def test_is_palindrome(values, expected):
head = list_build(values)
assert Solution().isPalindrome(head) is expected
Comment my solution¶
- Good:
- compares symmetric values from both ends.
- Problems:
- it adds a
prevattribute to nodes, which is not part of a singly linked list. - it needs a full pass to count and another to compare from the end, instead of using the standard reverse-half approach.
- it can fail conceptually in interview settings because modifying the node shape is usually considered invalid.
while right.next:assumesrightexists and is less robust than the standard slow/fast solution.
- it adds a
- Interview-wise:
- prefer reverse-second-half in place, because it matches the intended \(O(n)\) time and \(O(1)\) extra space solution cleanly.
from typing import Optional
import pytest
from utils import ListNode, list_build
class Solution:
def isPalindrome(self, head: Optional[ListNode],) -> bool:
left = head
right = head
count = 1
while right.next:
prev = right
right = right.next
right.prev = prev
count += 1
for i in range(count // 2):
if left.val != right.val:
return False
left = left.next
right = right.prev
return True
@pytest.mark.parametrize(
("values", "is_palindrome"),
[
([1,2,2,1], True),
([1,2], False)
],
)
def test_isPalindrome(values, is_palindrome):
head = list_build(values)
assert Solution().isPalindrome(head) == is_palindrome