Loading problems...
TL;DR: To solve this problem in space, find the middle of the list, reverse the second half in-place, and compare the two halves node by node.
The problem asks us to determine if a singly linked list reads the same forwards and backwards. Given the head of the list, we must return true if the sequence of values forms a palindrome and false otherwise. While checking for a palindrome is trivial with arrays or strings (by accessing indices from both ends), the singly linked list structure restricts us to forward-only traversal, making the LeetCode 234 solution more complex when optimizing for space.
The most intuitive way to solve the Palindrome Linked List problem is to transform the data structure into one that supports random access, such as an array or an .
ArrayListPseudo-code:
textfunction isPalindrome(head): values = [] current = head while current is not null: values.append(current.val) current = current.next left = 0 right = values.length - 1 while left < right: if values[left] != values[right]: return false left++ right-- return true
Complexity Analysis:
Why it fails: While this solution is correct and passes basic test cases, it requires extra memory. In an interview context, the constraint or follow-up question often specifically requests an space complexity solution. The brute force approach fails to meet this memory optimization requirement.
The core difficulty in LeetCode 234 is accessing the tail of the list and moving backwards while simultaneously moving forwards from the head.
To solve this without extra space, we can logically split the list into two halves. If the list is a palindrome, the first half must be identical to the reverse of the second half.
The algorithm relies on three main steps driven by the subpattern:
next pointers in the second half, we convert the structure so that traversing from the "end" (now the new head of the reversed part) moves us towards the middle.Visual Description:
Imagine the list 1 -> 2 -> 3 -> 2 -> 1.
The middle is 3. We split the list logically. The first half is 1 -> 2 -> 3. The second half is 2 -> 1.
We reverse the second half in-place. The list structure effectively becomes:
1 -> 2 -> 3 (First half)
1 -> 2 (Reversed second half, where 1 points to 2 and 2 points to null or 3).
We then iterate through both chains simultaneously. 1 matches 1, 2 matches 2.

slow and fast, at the head. Move fast two steps and slow one step until fast reaches the end. The slow pointer will effectively mark the start of the second half of the list.slow as the head of a sub-list. Use the standard iterative reversal logic (maintaining prev, curr, and next pointers) to reverse all nodes from slow to the end of the list. This transforms the list structure without allocating new nodes.first at the original head and second at the new head of the reversed half (the last node of the original list). Iterate through both. If values differ, return false.true.slow = head and fast = head.fast and fast.next are not null.fast = fast.next.next.slow = slow.next.slow is now at the middle (for odd lengths) or the start of the second half (for even lengths).prev = null and curr = slow.curr is not null:
next_temp = curr.next.curr.next to prev.prev to curr.curr to next_temp.prev is now the head of the reversed second half.left = head and right = prev.right is not null:
left.val != right.val, return false.left = left.next.right = right.next.true.The algorithm relies on the property that a palindrome is symmetric around its center. By reversing the second half, we map the -th node from the end to the -th node from the start of the reversed segment. Comparing the standard forward list with this reversed segment effectively compares with . Since the reversal is performed in-place using existing nodes, the space complexity remains constant. The pointers cover every node required for comparison, ensuring no mismatches are skipped.
cppclass Solution { public: bool isPalindrome(ListNode* head) { if (!head || !head->next) return true; // Step 1: Find the middle using Fast and Slow pointers ListNode* slow = head; ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } // Step 2: Reverse the second half in-place ListNode* prev = nullptr; ListNode* curr = slow; while (curr) { ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } // Step 3: Compare the first half and the reversed second half ListNode* firstHalf = head; ListNode* secondHalf = prev; // prev is the new head of the reversed half while (secondHalf) { if (firstHalf->val != secondHalf->val) { return false; } firstHalf = firstHalf->next; secondHalf = secondHalf->next; } return true; } };
javaclass Solution { public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) return true; // Step 1: Find the middle ListNode slow = head; ListNode fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // Step 2: Reverse the second half ListNode prev = null; ListNode curr = slow; while (curr != null) { ListNode nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } // Step 3: Compare ListNode p1 = head; ListNode p2 = prev; while (p2 != null) { if (p1.val != p2.val) { return false; } p1 = p1.next; p2 = p2.next; } return true; } }
pythonclass Solution: def isPalindrome(self, head: Optional[ListNode]) -> bool: if not head or not head.next: return True # Step 1: Find the middle slow = head fast = head while fast and fast.next: slow = slow.next fast = fast.next.next # Step 2: Reverse the second half prev = None curr = slow while curr: next_temp = curr.next curr.next = prev prev = curr curr = next_temp # Step 3: Compare left = head right = prev # prev is the head of the reversed part while right: if left.val != right.val: return False left = left.next right = right.next return True
slow, fast, prev, curr, next) regardless of the input size. No recursion stack or auxiliary data structures are used.The Linked List - In-place Reversal pattern and the technique of manipulating pointers without extra space are fundamental. Similar logic applies to:
next pointers based on value comparisons.Why does my solution get Time Limit Exceeded (TLE)?
while loops (e.g., forgetting curr = curr.next).Why does the comparison fail on even-length lists?
while (secondHalf != null)). In odd-length lists, the middle element is shared or ignored in the comparison, which this condition handles naturally.Is it okay to modify the input list?
Why do I get a NullPointerException during reversal?
curr.next without checking if curr is valid, or mishandling the head of the list in edge cases (like a single node).