Search code examples
pythonlinked-list

Why does this function not return True and instead loop around?


I am trying to solve LeetCode problem 234. Palindrome Linked List

Given the head of a singly linked list, return true if it is a palindrome or false otherwise.

My code worked for lists with an even amount of nodes. When I tried to change it with an if statement to advance one more node, the code started to loop. The code prints 'here' at the bottom but does not return True and instead loops and runs again.

There is an error with the tail.next = None line when it runs a second time.

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        if not head or not head.next:
            return True
        slow = fast = prev = head
        count = 0
        while fast and fast.next:
            prev = slow
            slow = slow.next
            fast = fast.next.next
            count += 1

        if count % 2 == 1:
            prev = slow
            slow = slow.next
        
        curr = slow
        tail = curr
        start = prev
        while curr:
            next_node = curr.next
            curr.next = prev
            prev = curr
            curr = next_node
        tail.next = None
        start.next = prev
        slow = fast = head
        for x in range(count):
            fast = fast.next
        while fast:
            if slow.val != fast.val:
                return False
            slow = slow.next
            fast = fast.next
        print('here')
        return True

Solution

  • There are several issues:

    • if count % 2 == 1: makes no sense. We can imagine there is a slight difference between a list with an even number of nodes and one with an odd number of nodes, but count does not represent the number of nodes in the list: it represents half the number of nodes in the list. And whether that count is odd or even has no relevant meaning.

    • Related to the previous point, if the list has two nodes, then count will be one, and so you'll set slow to None by this if block. This will result in an exception when executing tail.next = None as tail is None at that moment.

    • start.next = prev is a dangerous assignment. If the preceding while loop only makes one iteration, then start == prev and this assignment will create a loop. This happens if the previous point is resolved by removing tail.next = None to avoid the exception, and the input is a list with two nodes. The program will get into an infinite loop.

    Some other remarks:

    • It should not be necessary to keep a count. As explained above, it serves no purpose when wanting to know the parity of the list. And the loop that you have near the end, that is based on count, can be replaced by fast = prev, as just had just reversed the second half, and made prev the head of that second list.

    • You wrote "The code prints 'here' at the bottom but does not return True and instead loops and runs again.". This is a misunderstanding. It does return, but LeetCode continues to run a second test case.

    Here is a fix of your code:

    class Solution:
        def isPalindrome(self, head: Optional[ListNode]) -> bool:
            if not head or not head.next:
                return True
            # Find center: no need for a counter
            slow = fast = head
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
    
            # Reverse second half
            prev = None  # This is the way to ensure the reversed list ends with None
            while slow:
                next_node = slow.next
                slow.next = prev
                prev = slow
                slow = next_node
    
            # Compare halves, we can reuse `head`
            fast = prev  # No loop needed, we know where the second half starts
            while fast:
                if head.val != fast.val:
                    return False
                head = head.next
                fast = fast.next
            return True