Given the head
of a singly linked list, write a function to determine if it is a palindrome. A palindrome is a sequence that reads the same forwards and backward.
Example 1:
Input: head = [1,2,2,1]
Output: true
Explanation: The linked list represents the sequence 1 -> 2 -> 2 -> 1, which is a palindrome.
Example 2:
Input: head = [1,2]
Output: false
Explanation: The linked list represents the sequence 1 -> 2, which is not a palindrome.
Constraints:
[1, 10^5]
. Each node's value is between 0 and 9.Your task is to implement a function that takes the head of a singly linked list as input and returns true
if the list is a palindrome and false
otherwise. Optimize your solution for both time and space complexity. Can you achieve O(n) time and O(1) space complexity? What are the edge cases to consider, such as an empty list or a single-node list?
# Palindrome Linked List
## Problem Description
Given the `head` of a singly linked list, determine whether it is a palindrome.
## Naive Solution: Using an Array
1. **Copy the Linked List to an Array:** Traverse the linked list and store the value of each node in an array.
2. **Check for Palindrome:** Use two pointers, one at the beginning and one at the end of the array, to check if the array forms a palindrome.
```python
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def isPalindrome_naive(head: ListNode) -> bool:
arr = []
curr = head
while curr:
arr.append(curr.val)
curr = curr.next
left, right = 0, len(arr) - 1
while left < right:
if arr[left] != arr[right]:
return False
left += 1
right -= 1
return True
This approach avoids the O(n) space complexity by reversing the second half of the linked list in place.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def isPalindrome(head: ListNode) -> bool:
if not head or not head.next:
return True
# Find the middle of the linked list
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Reverse the second half of the linked list
prev, curr = None, slow
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
# Compare the first half and the reversed second half
first_half, second_half = head, prev
result = True
while second_half:
if first_half.val != second_half.val:
result = False
break
first_half = first_half.next
second_half = second_half.next
# Restore the linked list (optional)
prev, curr = None, prev
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
slow.next = prev # Connect the first half with the restored second half
return result
Therefore, the overall time complexity is O(n).
Example: 1 -> 2 -> 2 -> 1
Initial List:
Head -> 1 -> 2 -> 2 -> 1 -> None
Finding the Middle (Slow and Fast Pointers):
Slow: 1 -> 2
Fast: 1 -> 2 -> 1 (stops here)
Middle: 2
Reversing the Second Half:
Original Second Half: 2 -> 1 -> None
Reversed Second Half: 1 -> 2 -> None
Comparing the First and Reversed Second Halves:
First Half: 1 -> 2
Second Half: 1 -> 2
Result: True (Palindrome)