top of page

Palindrome Linked List

  • mosesg1123
  • May 2
  • 4 min read

Checking whether a linked list reads the same forwards and backwards is a great warm‑up that mirrors real‑world tasks like validating undo/redo buffers or symmetric data streams. It tests your ability to manipulate list pointers and handle data in linear time and constant space.


Problem Statement

Given the head of a singly linked list, determine whether the list is a palindrome. Return true if it reads the same backwards as forwards, or false otherwise. Aim for O(n) time and O(1) extra space.

// Definition for singly-linked list:
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

// Function signature in a technical interview:
public boolean isPalindrome(ListNode head);

1. Clarify Requirements Before Jumping Into Code

  • Can the list be empty? An empty list counts as a palindrome.

  • Single-node list? Also a palindrome.

  • Values are integers; negative or positive, but exact comparison only.

  • We must not modify node values; we can rewire pointers if we restore the list.

  • Extra space should be constant if possible; O(n) space is allowed but not ideal.


2. Identify the Category of the Question

This is a linked-list + two-pointer problem with an added in-place reversal or stack pattern. Common linked-list patterns include:

  • Two-pointer (fast/slow) to find mid

  • In-place reversal of part of the list

  • Dummy head for safe insertion/deletion

  • Stack to store node values

  • Recursion to simulate a stack

Recognizing “palindrome” suggests comparing mirrored halves, either via extra storage or by reversing half and then comparing.


3. Consider a Brute‑Force Approach to Understand the Problem

The simplest brute‑force: traverse the list, copy all values into an array or list, then use two indices to check whether values[i] == values[n-1-i] for all i.

  • Time: O(n) to collect + O(n) to compare = O(n)

  • Space: O(n) for the array

  • Pros: Very easy to implement

  • Cons: Uses O(n) extra memory


4. Brainstorm More Solutions

I’d like to reduce extra space. Options:


Stack of first half:

  • Use fast/slow to find midpoint.

  • Push slow’s values onto a stack while advancing fast two steps and slow one.

  • Then pop from stack and compare to second half.

  • Pros: O(n) time, O(n/2) space.

  • Cons: Still O(n) extra space.


What if the pointers in the second half of the linked list were reversed? Then I could traverse forward from the front pointer and backwards from the back pointer until they meet.

Reverse second half in-place:

  • Use fast/slow to find midpoint.

  • Reverse the second half of the list in-place.

  • Compare nodes from head and from reversed half one by one.

  • Restore the list by reversing the second half back.

  • Pros: O(n) time, O(1) space.

  • Cons: Pointer manipulation more complex, must restore to avoid side effects.


Recursive two-pointer:

  • Use recursion to traverse to end, compare on unwind with a front pointer.

  • Pros: Elegant code.

  • Cons: O(n) call stack, risk of overflow.


5. Discuss Trade‑Offs Between Your Solutions

Approach

Time

Space

Pros

Cons

Copy to array + compare

O(n)

O(n)

Simple, minimal pointer logic

Extra O(n) memory

Stack of first half

O(n)

O(n/2)

Simple logic, uses built-in stack

Still O(n) extra memory

Recursive comparison

O(n)

O(n) stack

Very concise

Call stack risk, O(n) memory

Reverse second half in-place

O(n)

O(1)

True constant space, restores original list

Pointer-reversal complexity

Reverse-second-half is optimal for O(1) space.


6. Write Pseudocode to Structure Your Thoughts

function isPalindrome(head):
  if head is null or head.next is null:
    return true

  // Find midpoint
  slow = head
  fast = head
  while fast.next != null and fast.next.next != null:
    slow = slow.next
    fast = fast.next.next

  // Reverse second half
  secondHead = reverseList(slow.next)

  // Compare halves
  p1 = head
  p2 = secondHead
  result = true
  while result and p2 != null:
    if p1.val != p2.val:
      result = false
    p1 = p1.next
    p2 = p2.next

  // Restore list
  slow.next = reverseList(secondHead)

  return result

7. Consider Edge Cases

  • Empty list → returns true

  • Single node → returns true

  • Two equal nodes → returns true

  • Two different nodes → returns false

  • Odd length palindrome → middle node ignored correctly

  • Even length palindrome → splits evenly

  • Non-palindrome → returns false quickly on mismatch


8. Write Full Code Syntax

public class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) return true;

        // 1. Find the end of first half
        ListNode firstHalfEnd = endOfFirstHalf(head);
        // 2. Reverse second half
        ListNode secondHalfStart = reverseList(firstHalfEnd.next);

        // 3. Check palindrome
        ListNode p1 = head;
        ListNode p2 = secondHalfStart;
        boolean result = true;
        while (result && p2 != null) {
            if (p1.val != p2.val) result = false;
            p1 = p1.next;
            p2 = p2.next;
        }

        // 4. Restore list
        firstHalfEnd.next = reverseList(secondHalfStart);
        return result;
    }

    private ListNode endOfFirstHalf(ListNode head) {
        ListNode slow = head, fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    private ListNode reverseList(ListNode head) {
        ListNode prev = null, curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }
}

9. Test Your Code

public static void main(String[] args) {
    Solution sol = new Solution();

    // Helper buildList(...) and toList(...) omitted for brevity

    // Test 1: empty list
    assert sol.isPalindrome(null) == true;

    // Test 2: single node
    assert sol.isPalindrome(buildList(1)) == true;

    // Test 3: two-node palindrome
    assert sol.isPalindrome(buildList(1,1)) == true;

    // Test 4: two-node non-palindrome
    assert sol.isPalindrome(buildList(1,2)) == false;

    // Test 5: odd-length palindrome
    assert sol.isPalindrome(buildList(1,2,3,2,1)) == true;

    // Test 6: even-length palindrome
    assert sol.isPalindrome(buildList(1,2,2,1)) == true;

    // Test 7: complex non-palindrome
    assert sol.isPalindrome(buildList(1,2,3,4,2,1)) == false;

    System.out.println("All tests passed!");
}

10. Key Lessons to Remember for Future Questions

  • Fast/slow pointer finds the midpoint in one pass.

  • In‑place reversal of the second half lets you compare mirrored nodes with O(1) space.

  • Always restore modified data structures if the problem requires no side effects.

  • Brute‑force or stack solutions clarify correctness before optimizing.

  • Clear pseudocode separating phases (find mid, reverse, compare, restore) prevents pointer errors.

  • Testing both odd and even lengths, as well as trivial cases, ensures full coverage.


Good Luck and Happy Coding!

Recent Posts

See All
Contains Duplicate Within K Distance

Checking for nearby repeated events in a log or sensor stream is a common task in monitoring, fraud detection, and real‑time analytics....

 
 
 
Longest Consecutive Sequence

Finding the longest run of consecutive days, IDs, or timestamps in a dataset comes up in analytics, event processing, and scheduling systems. This “Longest Consecutive Sequence” problem is a great way

 
 
 
Subarray Sum Equals K

Finding how many contiguous stretches of transactions sum to a target turns up in budgeting tools, analytics dashboards, and real‑time...

 
 
 

Комментарии


Drop Me a Line, Let Me Know What You Think

Thanks for submitting!

© 2023 by Train of Thoughts. Proudly created with Wix.com

bottom of page