Linked List
Palindrome Linked List
DESCRIPTION (inspired by Leetcode.com)
Given a reference of type ListNode which is the head of a singly linked list, write a function to determine if the linked list is a palindrome.
# Definition of a ListNode class ListNode: def __init__(self, value=0, next=None): self.value = value self.next = next
A linked list is a palindrome if the values of the nodes are the same when read from left-to-right and right-to-left. An empty list is considered a palindrome.
Example 1:
Output:
True
left-to-right: 5 -> 4 -> 3 -> 4 -> 5 right-to-left: 5 -> 4 -> 3 -> 4 -> 5
Example 2:
Output:
False
left-to-right: 5 -> 4 -> 3 right-to-left: 3 -> 4 -> 5
// class ListNode {
// int val;
// ListNode next;
// }
public class Solution {
public Boolean isPalindrome(ListNode head) {
// Your code goes here
}
}Run your code to see results here
Have suggestions or found something wrong?
We recommend taking some time to solve the problem on your own before reading the solutions below.
Solutions
To determine if a linked list is a palindrome, we need to compare the values of the nodes when read from left-to-right and right-to-left. But since our linked list is singly linked, we can only read its values from left-to-right.
One way around this problem is to convert the linked list to a list, which supports reading values from both directions.
1. Convert to List: Compare Reverse
If we convert the original linked list to a list, we can compare the list with its reverse to determine if it is a palindrome. If the list is a palindrome, the list and its reverse will be equal.
public boolean isPalindrome(ListNode head) {// convert linked list to listList<Integer> vals = new ArrayList<>();ListNode currentNode = head;while (currentNode != null) {vals.add(currentNode.val);currentNode = currentNode.next;}// compare list with its reverseList<Integer> reversed = new ArrayList<>(vals);Collections.reverse(reversed);return vals.equals(reversed);}
palindrome linked list
0 / 7
Time Complexity: O(n) where n is the number of nodes in the linked list. We iterate through each node in the linked list once to convert it to a list and then compare the list with its reverse, both of which are O(n) operations.
Space Complexity: O(n) where n is the number of nodes in the linked list, because we store the values of each node in a list.
2. Convert to List: Two-Pointer Technique
Instead of comparing the entire list with its reverse, we can use the two-pointer technique to check if the values in the list are a palindrome.
Like the previous solution, we first traverse the linked list and store the value of each node in a list.
Then, we initialize two pointers, left and right, at the start and end of the list, respectively. We compare the values at the left and right pointers. If they are equal, we move them both towards the center of the list. If the values at the left and right pointers are not equal, the list is not a palindrome.
public boolean isPalindrome(ListNode head) {// convert linked list to listList<Integer> vals = new ArrayList<>();ListNode current = head;while (current != null) {vals.add(current.val);current = current.next;}int left = 0, right = vals.size() - 1;while (left < right) {if (!vals.get(left).equals(vals.get(right))) {return false;}left++;right--;}return true;}
palindrome linked list
0 / 10
Time Complexity: O(n) where n is the number of nodes in the linked list. We iterate through each node in the linked list once to convert it to a list and then compare the values at the left and right pointers, which is also an O(n) operation. This is a slightly more time efficient solution than the previous one because we can stop as soon as we find a pair of values that are not equal during the palindrome check.
Space Complexity: O(n) where n is the number of nodes in the linked list. Like the previous solution, we store the values of each node in a list. This is also a slightly more space efficient solution than the previous one because we don't have to store the entire reverse of the list to compare it with the original.
3. Optimal Solution: Reverse Second Half
In the two-pointer above approach, we compared the first half of the list with the second half of the list, in reverse.
So if we first modify our linked list by reversing the direction of the nodes in the second half of the list, we can solve this problem without having to first convert the linked list to a list. This is the optimal solution, with a time complexity of O(n) and space complexity of O(1).
Step 1: Find the Middle of the Linked List
This step can be done using fast and slow pointers, which involves initializing two pointers, fast and slow, at the head of the linked list, and then iterating until fast reaches the end of the list. At each iteration, slow moves one node forward and fast moves two nodes forward. When fast reaches the tail node of the list, slow will point to the middle node in the list.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
initialize pointers
0 / 2
When there are an even number of nodes, fast will equal to None after moving past the tail node, and slow will be the first node of the second half of the list. In the case below, there are 4 nodes, so slow will point to the 3rd node.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
initialize pointers
0 / 2
Step 2: Reverse second half of list
At this point, slow points to the middle node in the list. Next, we want to reverse the direction of the pointers of each node starting from slow to the tail of the list.
Reversing the nodes in a linked list is a common problem that can be solved by iterating over each node that needs to be reversed. The key idea is to maintain three pointers, prev, curr, and next_, where prev points to the previous node, curr points to the node with the pointer we want to reverse, and next_ points to the next node in the iteration.
At each iteration, we:
- save the next node in the iteration by setting next_ = curr.next
- reverse the pointer by setting curr.next = prev
- move pointers for the next iteration by set curr = next_ and prev = curr
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
find middle node
0 / 10
You need the next_pointer to store the next node in the iteration before overwriting the value of curr.next with prev. If you don't store the next node in the iteration, you will lose the reference to the rest of the linked list.
Step 3: Check for Palindrome
With the nodes reversed, we can now use two pointers to compare the values of the nodes in the first half of the list with the reversed second half of the list.
At this point, prev points to the head of the reversed second half of the list. We can set a pointer left equal to the head of the original list and right equal to prev.
We then iterate through the list, comparing the values at left and right. If the values are not equal, the list is not a palindrome. If they are equal, we move left and right towards the center of the list, until they meet at the same node, at which point we return True because our list is a palindrome.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
curr = next_, prev = curr
0 / 5
Detecting the list is not a palindrome:
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
curr = next_, prev = curr
0 / 3
To recap:
- Find the middle of the linked list using the fast and slow pointers technique.
- Reverse the direction of the nodes in the second half of the linked list.
- Use two-pointers to check for a palindrome by comparing the values of the nodes in the first half of the list with the reversed second half of the list.
Implementation
Here's the complete optimal solution that implements the three-step approach:
public boolean isPalindrome(ListNode head) {// Find middle of the linked list using fast and slow pointersListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// Reverse second half of the listListNode prev = null;while (slow != null) {ListNode next = slow.next; // Save next nodeslow.next = prev; // Reverse pointerprev = slow; // Move pointersslow = next;}// Check palindrome by comparing halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
Code
To construct the linked list that is used in the animation below, provide a list of integers nodes. Each integer in nodes is used as the value of a node in the linked list, and the order of the integers in the list will be the order of the nodes in the linked list.
For example, if nodes = [1, 2, 3], the linked list will be 1 -> 2 -> 3.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
palindrome linked list
0 / 18
Edge Cases
Empty List
When the linked list is empty, all of the pointers will be None, and the function returns True immediately because an empty list is a palindrome.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
palindrome linked list
0 / 4
Single Node
When the linked list has a single node:
- fast.next is None, so the first while loop to find the middle node doesn't execute.
- Reversing the second half of the list has no effect because there is only one node.
- The palindrome check returns True because a single node is a palindrome.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
palindrome linked list
0 / 8
Two Nodes (Palindrome)
When the linked list has two nodes:
- The first while loop sets the slow pointer to the second node.
- Reversing the second half of the list has no effect slow.next is None already.
- The palindrome check compares the values of the two nodes and returns True if they are equal.
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
palindrome linked list
0 / 9
Two Nodes (Not Palindrome)
public boolean isPalindrome(ListNode head) {if (head == null || head.next == null) {return true;}// find middle nodeListNode slow = head, fast = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// reverse second halfListNode prev = null;while (slow != null) {ListNode next = slow.next;slow.next = prev;prev = slow;slow = next;}// compare halvesListNode first = head, second = prev;while (second != null) {if (first.val != second.val) {return false;}first = first.next;second = second.next;}return true;}
palindrome linked list
0 / 8
What is the time complexity of this solution?
O(n)
O(2ⁿ)
O(n³)
O(4ⁿ)
Trade-off: The O(1) space solution mutates the input
The O(1) space solution comes with a significant practical downside: it mutates the input linked list. During execution, the second half of the list is reversed in-place, which means any other code holding a reference to nodes in that portion of the list will see a corrupted structure while the check is running.
You could reverse the second half again after the palindrome check to restore the original list, but that's additional O(n) work and still isn't safe in concurrent or multi-threaded contexts where another thread might read the list mid-mutation.
In real-world code, mutating an input data structure passed to a read-only query function like isPalindrome is almost always unacceptable. If you need to avoid mutation, you're back to O(n) space — whether that's copying the list, collecting values into an array, or using a stack. The array-based solutions (Solutions 1 and 2 above) are typically the better choice in practice for this reason.
The O(1) space approach is worth knowing because interviewers often ask for it specifically, and it demonstrates important linked list manipulation techniques (fast/slow pointers, in-place reversal). Just be ready to discuss why you might not use it in production.