Given the head of a singly linked list, determine if it's a palindrome.

Example 1:

Image represents a directed graph illustrating a sequence of states or steps.  Five circular nodes, labeled sequentially from 1 to 5, are arranged horizontally. Each node contains a single digit representing a state.  Directed edges, represented by arrows, connect the nodes, indicating the flow of information or progression through the sequence. The arrows point from node 1 to node 2, from node 2 to node 3, from node 3 to node 4 (which is also labeled 2), and finally from node 4 to node 5 (which is labeled 1).  This suggests a cyclical pattern where the sequence starts with state 1, progresses through states 2 and 3, then returns to state 2 before finally returning to the initial state 1.  The repetition of states 2 and 1 highlights a potential loop or recurring pattern within the overall process.
Output: True

Example 2:

Image represents a directed graph illustrating a sequence of operations or states.  The graph consists of four circular nodes, each containing a single digit: '1', '2', '1', and '2'.  These nodes are connected by directed edges, represented by arrows.  The first node, containing '1', is connected to the second node ('2') via a directed edge pointing from '1' to '2'.  Similarly, the second node ('2') connects to the third node ('1'), and the third node ('1') connects to the fourth node ('2'), each connection represented by a directed edge showing the flow from left to right.  The arrangement suggests a pattern of alternating '1' and '2' states or operations, with the information flow unidirectional from left to right.
Output: False

In Plain English

New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.

🌱 Real-world analogy: folding a strip of paper

Write the numbers along a paper strip, then fold it in half. If every number lands exactly on its mirror twin, the strip is a palindrome. We can't literally fold a linked list, but we can do the next best thing: reverse the back half so it lines up head-to-head with the front half, then check the two halves match number by number.

Intuition

A linked list would be palindromic if its values read the same forward and backward. A naive way to check this would be to store all the values of the linked list in an array, allowing us to freely traverse these values forward and backward to confirm if it’s palindromic. However, this takes linear space. Instead, it would be better if we had a way to traverse the linked list in reverse order to confirm if it's a palindrome. Is there a way to go about this?

Going off the above definition, we know that if a linked list is a palindrome, reversing it would result in the same sequence of values.

Image represents two linked lists displayed horizontally.  The top list, labeled 'linked list:', shows a sequence of nodes containing the values 1, 2, 3, 2, and 1, connected by rightward-pointing arrows indicating the direction of traversal. The bottom list, labeled 'reversed linked list:', displays the same numerical values (1, 2, 3, 2, 1) but in reverse order, connected by leftward-pointing arrows.  A horizontal orange arrow connects the ends of both lists, labeled 'same sequence of values,' highlighting that both lists contain the identical numerical sequence, but in opposite directions, demonstrating the concept of reversing a linked list.

This means we could create a copy of the linked list, reverse it, and compare its values with the original linked list. However, this would still take up linear space. Can we adjust this idea to avoid creating a new linked list?

An important observation is that we only need to compare the first half of the original linked list with the reverse of the second half (if there are an odd number of elements, we can just include the middle node in both halves) to check if the linked list is a palindrome:

Image represents a diagram illustrating a palindrome checking algorithm.  Five circular nodes, numbered sequentially 1, 2, 3, 2, 1, are arranged linearly and connected by unidirectional arrows.  The arrows indicate the flow of data.  Nodes 1 and 2 are connected by an arrow pointing from 1 to 2, representing the processing of the first half of the input. Node 2 is connected to node 3, also with an arrow pointing from 2 to 3, continuing the processing of the first half. A grey line segment above nodes 1, 2, and 3 is labeled 'compare first half'.  Node 3 is connected to node 2 by an arrow pointing from 2 to 3, and node 2 is connected to node 1 by an arrow pointing from 1 to 2, representing the reversed processing of the second half. A grey line segment below nodes 3, 2, and 1 is labeled 'with reverse of second half'. The overall structure shows a comparison between the first half of a sequence and the reversed second half to determine if it's a palindrome.

Before we can perform this comparison, we need to:

  1. Find the middle of the linked list to get the head of the second half.
  2. Reverse the second half of the linked list from this middle node.

Notice that step 2 involves modifying the input. In this problem, let’s assume this is acceptable. However, it's always good to check with the interviewer if changing the input is allowed before moving forward with the solution.

Now, let’s see how these two steps can be applied. Start by obtaining the middle node (mid) of the linked list.

Image represents a linked list data structure with five nodes.  Each node is depicted as a circle containing a numerical value (1, 2, 3, 2, 1).  Arrows indicate the directional links between nodes, showing the sequence of the list.  A gray rectangular box labeled 'head' points to the first node (containing the value 1), representing the starting point of the list.  Separately, an orange rectangular box labeled 'mid' points to the third node (containing the value 3), highlighting a specific node within the list.  The arrangement visually demonstrates the linear progression of elements in a linked list, with the 'head' and 'mid' labels indicating pointers to specific locations within that sequence.

To learn how to get to the middle of a linked list, read the explanation in the Linked List Midpoint problem in the Fast and Slow Pointers chapter.


Then, reverse the second half of the linked list starting at mid. The last node of the original linked list becomes the head of the second half. This second head is used to traverse the newly reversed second half.

Image represents a diagram illustrating a linked list data structure with two separate heads.  The first head, labeled 'head' in a gray rectangle, points to a node containing the number '1'. This node is connected via a black arrow to a node containing '2', which is further connected to a node containing '3' by another black arrow. A second head, labeled 'mid' in a gray rectangle, points to the node containing '3'.  Separately, a third head, labeled 'second_head' in an orange rectangle, points to a node containing '1'. This node is connected to a node containing '2' via a light-blue arrow pointing left, indicating a connection in the opposite direction compared to the first sequence.  The overall structure shows two distinct linked lists, one progressing from '1' to '3' and the other starting from '1' and connecting to '2' in reverse order, sharing some nodes but with different starting points and directions of traversal.

To learn how to reverse a linked list in O(n)O(n) time, read the explanation in the Reverse Linked List problem in this chapter.


The last thing we need to do is check if the first half matches the now-reversed second half. We can do this by simultaneously traversing both halves node by node, and comparing each node from the first half to the corresponding node from the second half. If at any point the node values don't match, it indicates the linked list is not a palindrome.

We can use two pointers (ptr1 and ptr2) to iterate through the first and the reversed second half of the linked list, respectively:

Image represents a linked list data structure with two pointers, `ptr1` and `ptr2`, manipulating its nodes.  The list consists of five nodes, numbered 1, 2, 3, 2, and 1, sequentially connected by solid arrows indicating the primary list order. A rectangular box labeled 'head' points to the first node (1), establishing the beginning of the list.  Another rectangular box labeled 'second_head' points to the last node (1), suggesting a second entry point.  Dashed orange arrows show additional connections: `ptr1` points to node 3, and `ptr2` points to node 3.  Furthermore, dashed orange arrows illustrate a secondary, non-sequential connection from node 1 to node 2, and from node 2 to node 3, and from node 2 to node 1, indicating a potential circular or rearranged structure within the list, possibly created or modified by the pointers.

Implementation

from ds import ListNode
    
def palindromic_linked_list(head: ListNode) -> bool: 
    # Find the middle of the linked list and then reverse the second half of the # linked list starting at this midpoint.
    mid = find_middle(head)
    second_head = reverse_list(mid)
    # Compare the first half and the reversed second half of the list
    ptr1, ptr2 = head, second_head
    res = True
    while ptr2:
        if ptr1.val != ptr2.val:
            res = False
        ptr1, ptr2 = ptr1.next, ptr2.next
    return res
    
# From the 'Reverse Linked List' problem.
def reverse_list(head: ListNode) -> ListNode:
    prevNode, currNode = None, head
    while currNode:
        nextNode = currNode.next
        currNode.next = prevNode
        prevNode = currNode
        currNode = nextNode
    return prevNode
    
# From the 'Linked List Midpoint' problem.
def find_middle(head: ListNode) -> ListNode:
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow
bool palindromicLinkedList(ListNode* head) {
    // 1. find the middle
    ListNode* slow = head;
    ListNode* fast = head;
    while (fast != nullptr && fast->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
    }
    // 2. reverse from slow to the end
    ListNode* prev = nullptr;
    while (slow != nullptr) {
        ListNode* nxt = slow->next;
        slow->next = prev;
        prev = slow;
        slow = nxt;
    }
    // 3. compare the two halves
    ListNode* left = head;
    ListNode* right = prev;
    while (right != nullptr) {
        if (left->val != right->val) return false;
        left = left->next;
        right = right->next;
    }
    return true;
}
public boolean palindromicLinkedList(ListNode head) {
    // 1. find the middle
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    // 2. reverse from slow to the end
    ListNode prev = null;
    while (slow != null) {
        ListNode nxt = slow.next;
        slow.next = prev;
        prev = slow;
        slow = nxt;
    }
    // 3. compare the two halves
    ListNode left = head, right = prev;
    while (right != null) {
        if (left.val != right.val) return false;
        left = left.next;
        right = right.next;
    }
    return true;
}

Complexity Analysis

Time complexity: The time complexity of palindromic_linked_list is O(n)O(n), where nn denotes the length of the linked list. This is because it involves iterating through the linked list three times: once to find the middle node, once to reverse the second half, and once more to compare the two halves.

Space complexity: The space complexity is O(1)O(1).

Dry Runs

Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.

✅ Dry run 1 (valid) — 1 → 2 → 3 → 2 → 1 reads the same both ways → true.

slow / left fast / right

1. Find the middle. slow +1, fast +2 each step; fast falls off → slow rests on the middle (3).
1
2
3slow
2
1fast→null
2. Reverse the second half (from the middle): 3 → 2 → 1 becomes 1 → 2 → 3.
front
1
2
3
back (reversed)
1prev
2
3
3. Compare inward. (1,1) ✓ then (2,2) ✓ then middle — all match → palindrome. 🎉
1left
2
3
2
1right

The same run as a table

Phase 1 finds the middle; phase 3 compares front vs. reversed back.
phaseslow / leftfast / rightcheck
mid: start11
mid: step23fast.next ok
mid: step31 (last)fast.next null → stop
cmp11equal
cmp22equal
cmp3nullright null → return true
📈 Dry run 2 (invalid) — 1 → 2 → 3 → 4 → 1 is NOT a palindrome → false.

slow / left fast / right mismatch

1. Find the middle. slow +1, fast +2 each step; fast falls off → slow rests on the middle (3).
1
2
3slow
4
1fast→null
2. Reverse the second half (from the middle): 3 → 4 → 1 becomes 1 → 4 → 3.
front
1
2
3
back (reversed)
1prev
4
3
3. Compare inward. (1,1) ✓ then (2,4) ✗ — a mismatch! Return false immediately; the middle node is never even checked.
front
1left
2
3
back (reversed)
1right
4
3

The same run as a table

A mismatch during comparison short-circuits: we return false without scanning the rest.
phaseslow / leftfast / rightcheck
mid: start11
mid: step23fast.next ok
mid: step31 (last)fast.next null → stop
cmp11equal
cmp (stop)242 ≠ 4 → return false

Contrast with Dry run 1: there every pair matched and the loop ran to the end → true. Here the first pair (1,1) matches but the second (2,4) differs, so we return false immediately — the middle node is never inspected. Mismatches short-circuit.

The same steps in each language

Both runs step exactly as the tables above. Python compares left.val != right.val and returns False on the first mismatch; the loop runs while right is not None.

Same steps. C++: if (left->val != right->val) return false; looping while (right != nullptr).

Same steps. Java: if (left.val != right.val) return false; looping while (right != null).

Interview Tip

Tip: Confirm if it’s acceptable to modify the linked list. In our solution, we reversed the second half of the linked list which dismantled the input’s initial structure. Why does this matter? Oftentimes, the input data structure should not be modified, particularly if it's shared or accessed concurrently. As such, it’s important to confirm with your interviewer whether input modification is acceptable and to briefly address the implications of this.