Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Linked List Traversal - Understanding how to iterate through a singly linked list
Two Pointers (Fast & Slow) - Used to find the middle of the list in one pass
In-place List Reversal - Reversing a linked list without extra space
Stack Data Structure - LIFO behavior useful for comparing elements from both ends
1. Convert To Array
Intuition
A palindrome reads the same forwards and backwards. Linked lists only allow forward traversal, making direct comparison difficult. The simplest approach is to convert the linked list to an array where we can use random access.
Once we have an array, we can use two pointers from both ends moving toward the center, comparing values as we go.
Algorithm
Traverse the linked list and store all node values in an array.
Initialize two pointers: left at the start and right at the end of the array.
While left < right, compare arr[left] and arr[right]. If they differ, return false.
Move left forward and right backward after each comparison.
# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defisPalindrome(self, head: Optional[ListNode])->bool:
arr =[]
cur = head
while cur:
arr.append(cur.val)
cur = cur.next
l, r =0,len(arr)-1while l < r:if arr[l]!= arr[r]:returnFalse
l, r = l +1, r -1returnTrue
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicbooleanisPalindrome(ListNode head){List<Integer> arr =newArrayList<>();ListNode cur = head;while(cur !=null){
arr.add(cur.val);
cur = cur.next;}int l =0, r = arr.size()-1;while(l < r){if(!arr.get(l).equals(arr.get(r))){returnfalse;}
l++;
r--;}returntrue;}}
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {boolean}
*/isPalindrome(head){const arr =[];let cur = head;while(cur){
arr.push(cur.val);
cur = cur.next;}let l =0,
r = arr.length -1;while(l < r){if(arr[l]!== arr[r]){returnfalse;}
l++;
r--;}returntrue;}}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicboolIsPalindrome(ListNode head){List<int> arr =newList<int>();ListNode cur = head;while(cur !=null){
arr.Add(cur.val);
cur = cur.next;}int l =0, r = arr.Count -1;while(l < r){if(arr[l]!= arr[r]){returnfalse;}
l++;
r--;}returntrue;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcisPalindrome(head *ListNode)bool{
arr :=[]int{}
cur := head
for cur !=nil{
arr =append(arr, cur.Val)
cur = cur.Next
}
l, r :=0,len(arr)-1for l < r {if arr[l]!= arr[r]{returnfalse}
l++
r--}returntrue}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funisPalindrome(head: ListNode?): Boolean {val arr = mutableListOf<Int>()var cur = head
while(cur !=null){
arr.add(cur.`val`)
cur = cur.next
}var l =0var r = arr.size -1while(l < r){if(arr[l]!= arr[r]){returnfalse}
l++
r--}returntrue}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcisPalindrome(_ head:ListNode?)->Bool{var arr =[Int]()var cur = head
while cur !=nil{
arr.append(cur!.val)
cur = cur?.next
}var l =0var r = arr.count -1while l < r {if arr[l]!= arr[r]{returnfalse}
l +=1
r -=1}returntrue}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
2. Recursion
Intuition
We can use recursion to simulate traversing from the end of the list. By recursing to the end first and comparing on the way back, we effectively compare nodes from both ends simultaneously.
We maintain a pointer starting at the head. As recursion unwinds from the tail, we compare each node with the head pointer and advance the head pointer after each match.
Algorithm
Initialize a pointer cur at the head of the list.
Define a recursive function rec(node) that:
Returns true if node is null (base case).
Recursively calls rec(node.next) first to reach the end.
Compares cur.val with node.val. If they differ, return false.
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {boolean}
*/isPalindrome(head){let cur = head;constrec=(node)=>{if(node !==null){if(!rec(node.next)){returnfalse;}if(cur.val !== node.val){returnfalse;}
cur = cur.next;}returntrue;};returnrec(head);}}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{privateListNode cur;publicboolIsPalindrome(ListNode head){
cur = head;returnRec(head);}privateboolRec(ListNode node){if(node !=null){if(!Rec(node.next)){returnfalse;}if(cur.val != node.val){returnfalse;}
cur = cur.next;}returntrue;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcisPalindrome(head *ListNode)bool{
cur := head
var rec func(node *ListNode)bool
rec =func(node *ListNode)bool{if node !=nil{if!rec(node.Next){returnfalse}if cur.Val != node.Val {returnfalse}
cur = cur.Next
}returntrue}returnrec(head)}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {privatevar cur: ListNode?=nullfunisPalindrome(head: ListNode?): Boolean {
cur = head
returnrec(head)}privatefunrec(node: ListNode?): Boolean {if(node !=null){if(!rec(node.next)){returnfalse}if(cur?.`val` != node.`val`){returnfalse}
cur = cur?.next
}returntrue}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{var cur:ListNode?funcisPalindrome(_ head:ListNode?)->Bool{
cur = head
returnrec(head)}privatefuncrec(_ node:ListNode?)->Bool{if node !=nil{if!rec(node?.next){returnfalse}if cur?.val != node?.val {returnfalse}
cur = cur?.next
}returntrue}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n) for recursion stack.
3. Stack
Intuition
A stack provides LIFO (last-in-first-out) access. If we push all elements onto a stack and then traverse the list while popping, we compare elements from the front and back simultaneously.
This is similar to the array approach but uses a stack to reverse the order of comparison.
Algorithm
Traverse the linked list and push all values onto a stack.
Traverse the list again from the head. For each node, pop from the stack and compare.
If any comparison fails, return false.
If we complete the traversal without mismatches, return true.
# Definition for singly-linked list.# class ListNode:# def __init__(self, val=0, next=None):# self.val = val# self.next = nextclassSolution:defisPalindrome(self, head: Optional[ListNode])->bool:
stack =[]
cur = head
while cur:
stack.append(cur.val)
cur = cur.next
cur = head
while cur and cur.val == stack.pop():
cur = cur.nextreturnnot cur
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicbooleanisPalindrome(ListNode head){Stack<Integer> stack =newStack<>();ListNode cur = head;while(cur !=null){
stack.push(cur.val);
cur = cur.next;}
cur = head;while(cur !=null&& cur.val == stack.pop()){
cur = cur.next;}return cur ==null;}}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/classSolution{public:boolisPalindrome(ListNode* head){
stack<int> stack;
ListNode* cur = head;while(cur !=nullptr){
stack.push(cur->val);
cur = cur->next;}
cur = head;while(cur !=nullptr&& cur->val == stack.top()){
stack.pop();
cur = cur->next;}return cur ==nullptr;}};
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {boolean}
*/isPalindrome(head){const stack =[];let cur = head;while(cur){
stack.push(cur.val);
cur = cur.next;}
cur = head;while(cur && cur.val === stack.pop()){
cur = cur.next;}return cur ===null;}}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicboolIsPalindrome(ListNode head){Stack<int> stack =newStack<int>();ListNode cur = head;while(cur !=null){
stack.Push(cur.val);
cur = cur.next;}
cur = head;while(cur !=null&& cur.val == stack.Pop()){
cur = cur.next;}return cur ==null;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcisPalindrome(head *ListNode)bool{
stack :=[]int{}
cur := head
for cur !=nil{
stack =append(stack, cur.Val)
cur = cur.Next
}
cur = head
for cur !=nil&& cur.Val == stack[len(stack)-1]{
stack = stack[:len(stack)-1]
cur = cur.Next
}return cur ==nil}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funisPalindrome(head: ListNode?): Boolean {val stack = ArrayDeque<Int>()var cur = head
while(cur !=null){
stack.addLast(cur.`val`)
cur = cur.next
}
cur = head
while(cur !=null&& cur.`val` == stack.removeLast()){
cur = cur.next
}return cur ==null}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcisPalindrome(_ head:ListNode?)->Bool{var stack =[Int]()var cur = head
while cur !=nil{
stack.append(cur!.val)
cur = cur?.next
}
cur = head
while cur !=nil&& cur!.val == stack.removeLast(){
cur = cur?.next
}return cur ==nil}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
4. Fast & Slow Pointers
Intuition
To achieve O(1) extra space, we can reverse the second half of the list in place. Then we compare the first half with the reversed second half node by node.
We use the fast and slow pointer technique to find the middle of the list. The fast pointer moves twice as fast, so when it reaches the end, the slow pointer is at the middle.
Algorithm
Use fast and slow pointers to find the middle of the list. When fast reaches the end, slow is at the middle.
Reverse the second half of the list starting from slow.
Compare nodes from the head and from the reversed second half. If any values differ, return false.
Continue until the reversed half is fully traversed.
/**
* Definition for singly-linked list.
* class ListNode {
* constructor(val = 0, next = null) {
* this.val = val;
* this.next = next;
* }
* }
*/classSolution{/**
* @param {ListNode} head
* @return {boolean}
*/isPalindrome(head){let fast = head,
slow = head;// find middle (slow)while(fast && fast.next){
fast = fast.next.next;
slow = slow.next;}// reverse second halflet prev =null;while(slow){let tmp = slow.next;
slow.next = prev;
prev = slow;
slow = tmp;}// check palindromelet left = head,
right = prev;while(right){if(left.val !== right.val){returnfalse;}
left = left.next;
right = right.next;}returntrue;}}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/publicclassSolution{publicboolIsPalindrome(ListNode head){ListNode fast = head, slow = head;while(fast !=null&& fast.next !=null){
fast = fast.next.next;
slow = slow.next;}ListNode prev =null;while(slow !=null){ListNode tmp = slow.next;
slow.next = prev;
prev = slow;
slow = tmp;}ListNode left = head, right = prev;while(right !=null){if(left.val != right.val){returnfalse;}
left = left.next;
right = right.next;}returntrue;}}
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/funcisPalindrome(head *ListNode)bool{
fast, slow := head, head
// find middle (slow)for fast !=nil&& fast.Next !=nil{
fast = fast.Next.Next
slow = slow.Next
}// reverse second halfvar prev *ListNode
for slow !=nil{
tmp := slow.Next
slow.Next = prev
prev = slow
slow = tmp
}// check palindrome
left, right := head, prev
for right !=nil{if left.Val != right.Val {returnfalse}
left = left.Next
right = right.Next
}returntrue}
/**
* Example:
* var li = ListNode(5)
* var v = li.`val`
* Definition for singly-linked list.
* class ListNode(var `val`: Int) {
* var next: ListNode? = null
* }
*/class Solution {funisPalindrome(head: ListNode?): Boolean {var fast = head
var slow = head
// find middle (slow)while(fast !=null&& fast.next !=null){
fast = fast.next?.next
slow = slow?.next
}// reverse second halfvar prev: ListNode?=nullvar curr = slow
while(curr !=null){val tmp = curr.next
curr.next = prev
prev = curr
curr = tmp
}// check palindromevar left = head
var right = prev
while(right !=null){if(left?.`val` != right.`val`){returnfalse}
left = left?.next
right = right.next
}returntrue}}
/**
* Definition for singly-linked list.
* public class ListNode {
* public var val: Int
* public var next: ListNode?
* public init() { self.val = 0; self.next = nil; }
* public init(_ val: Int) { self.val = val; self.next = nil; }
* public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
* }
*/classSolution{funcisPalindrome(_ head:ListNode?)->Bool{var fast = head
var slow = head
// find middle (slow)while fast !=nil&& fast?.next !=nil{
fast = fast?.next?.next
slow = slow?.next
}// reverse second halfvar prev:ListNode?=nilvar curr = slow
while curr !=nil{let tmp = curr?.next
curr?.next = prev
prev = curr
curr = tmp
}// check palindromevarleft= head
varright= prev
whileright!=nil{ifleft?.val !=right?.val {returnfalse}left=left?.next
right=right?.next
}returntrue}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1) extra space.
Common Pitfalls
Incorrect Middle Finding for Odd-Length Lists
When using fast and slow pointers, for odd-length lists the slow pointer ends up at the exact middle element. If you reverse starting from slow, the middle element ends up in the reversed half. This still works because we only compare until the reversed half is exhausted, but misunderstanding this can lead to off-by-one errors in the comparison loop.
Not Restoring the Original List
The in-place reversal approach modifies the original linked list structure. If the problem requires the list to remain unchanged after the function call, or if subsequent operations depend on the original structure, you must reverse the second half back to its original order after the palindrome check.