Given the head
of a singly linked list, return true
if it is a palindrome or false
otherwise.
Example 1:
Input: head = [1,2,2,1] Output: true
Example 2:
Input: head = [1,2] Output: false
Constraints:
[1, 105]
.0 <= Node.val <= 9
O(n)
time and O(1)
space?When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
To check if a linked list is a palindrome using brute force, we essentially write down the list's values and then see if they read the same forwards and backward. We explore every possible way to verify this.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def is_palindrome_brute_force(head):
list_of_values = []
current_node = head
# Iterate through the linked list and store values in a list
while current_node:
list_of_values.append(current_node.val)
current_node = current_node.next
left_index = 0
right_index = len(list_of_values) - 1
# Iterate until the middle of the list is reached
while left_index < right_index:
# Compare values from both ends
if list_of_values[left_index] != list_of_values[right_index]:
# Values don't match, not a palindrome
return False
left_index += 1
right_index -= 1
# All values matched; it's a palindrome
return True
To check if a linked list is a palindrome efficiently, we don't need to compare every single element. Instead, we can split the list in half, reverse the second half, and then compare the two halves to see if they match.
Here's how the algorithm would work step-by-step:
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_pointer = head
fast_pointer = head
while fast_pointer and fast_pointer.next:
slow_pointer = slow_pointer.next
fast_pointer = fast_pointer.next.next
# Reverse the second half of the linked list.
previous_node = None
current_node = slow_pointer
while current_node:
next_node = current_node.next
current_node.next = previous_node
previous_node = current_node
current_node = next_node
# Compare the first half with the reversed second half.
first_half_pointer = head
second_half_pointer = previous_node
while second_half_pointer:
if first_half_pointer.val != second_half_pointer.val:
return False
first_half_pointer = first_half_pointer.next
second_half_pointer = second_half_pointer.next
return True
Case | How to Handle |
---|---|
Empty list | Return True immediately since an empty list is considered a palindrome. |
Single node list | Return True immediately since a single node list is also considered a palindrome. |
Two node list with same values | Return True as the list is a palindrome. |
Two node list with different values | Return False as the list is not a palindrome. |
List with a large number of nodes (memory constraints) | Iterative solutions using constant extra space are preferred for very large lists to avoid stack overflow or memory issues with recursive calls. |
List with all nodes having the same value | Should return True as it is a palindrome, and algorithm needs to handle this without infinite loops or incorrect comparisons. |
List with integer overflow potential (if comparing node values directly) | Avoid direct value comparison if overflow is a concern; compare structure (node references) or use a more robust method like converting to string. |
List containing negative or zero values | The algorithm should correctly handle negative and zero values as palindrome checks are based on structure, not value ranges. |