3

I'm trying to check if a singly linked list is a palindrome or not. The constraints are - the algorithm has to be in linear time and constant space.

The basic algorithm I'm using is as below -

  1. Use fast & slow pointers to divide the list into two halves.
  2. Reverse the second half in-place.
  3. Compare the first and second half.
  4. Construct back the original list
  5. Return the result.

My implementation works for when the list has even number of elements but fails if the number of elements is odd.

/*
 * @brief   Checks if a list is a palindrome or not
 */
bool is_palindrome(node *head)
{
    node *first;                /* Points to head node of 1st half */
    node *second;               /* Points to head node of 2nd half */
    node *f_ptr = head;         /* Fast pointer */
    node *s_ptr = head;         /* Slow pointer */
    node *prev = NULL;          /* Previous to slow pointer */
    bool ret = false;           /* Return value */

    while (f_ptr && f_ptr->next && f_ptr->next->next)
    {
        prev = s_ptr;
        s_ptr = s_ptr->next;
        f_ptr = f_ptr->next->next;
    }

    /* List with even number of elements */
    if (!(f_ptr->next->next))
    {
        first = head;
        second = s_ptr->next;
        s_ptr->next = NULL;
        /* Reverse the second half */
        second = reverse_list(&second);
        /* Compare the first & second half */
        ret = are_identical(first, second);
        /* Finally, construct the original list back */
        second = reverse_list(&second);
        s_ptr->next = second;
        print_list(head);
    }
    /* List with odd number of elements */
    if (!(f_ptr->next))
    {
        first = head;
        second = s_ptr->next;
        prev->next = NULL;
        s_ptr->next = NULL;
        /* Reverse the second half */
        second = reverse_list(&second);
        /* Compare the first & second half */
        ret = are_identical(first, second);
        /* Finally, construct the original list back */
        second = reverse_list(&second);
        prev->next = s_ptr; s_ptr->next = second;
        print_list(head);
    }
    return ret;
}

Can someone please help me figure out what I am doing wrong while handling the odd number case? The complete implementation of this program can be found here.

rurtle
  • 411
  • 4
  • 18
  • I think you need to rework your search of second half's first node. It needs either to point oughtstraight to middle node (even case) or to 'N div 2 + 1' node (odd case). Then you just rotate half and traverse it. IMHO you don't need any special processing for odd case since no rules are set for this specific node. – HighPredator Jun 08 '16 at 13:32
  • You can use this solution: http://www.geeksforgeeks.org/function-to-check-if-a-singly-linked-list-is-palindrome/ – ReshaD Jun 08 '16 at 13:33
  • 2
    What to do mean when you say your implementation "fails if the number of elements is odd"? What is happening? Program crash? Incorrect result? – s7amuser Jun 08 '16 at 13:33
  • If the number of elements is odd, then isn't it NOT palindrome by definition? Otherwise, if the middle (odd) element is considered symmetric, then you can just leave it out and check the two halves (that are now of equal length). – Paul Ogilvie Jun 08 '16 at 13:44
  • @PaulOgilvie, the usual definition of a palindrome is that it is the same backwards as it is forwards. That certainly does accommodate odd-length palindromes, wherein, as you say, the middle element does not affect the result. – John Bollinger Jun 08 '16 at 14:15
  • 1
    It might be a bit easier to reverse the first half of the list (which you can do in conjunction with splitting the list into halves) instead of the second. Either way, when the list has an odd number of elements then, as Paul said, the middle element does not contribute to the result. That appears to be where you are going wrong. – John Bollinger Jun 08 '16 at 14:20
  • @s7amuser It segfaults. And it happens in the conditional block `if (!(f_ptr->next))`. Trying to narrow it down. @JohnBollinger Nice suggestion. I'll definitely give that a try. – rurtle Jun 08 '16 at 14:27
  • 1
    I assumed it segfaults but it should be because of this `if !(f_ptr->next->next)`. If the list has an odd number of elements, then `f_ptr` will point to the last one after the first loop finishes. Thus, `f_ptr->next` will be `NULL` so `f_ptr->next->next` will segfault. Try checking first `!(f_ptr->next)` and only after `!(f_ptr->next->next)`. – s7amuser Jun 08 '16 at 14:33
  • @s7amuser Whoa! Good catch. That indeed was the issue. Changing the order of the conditional blocks and adding `else if` instead of plain `if` did the trick. Thank you. – rurtle Jun 08 '16 at 14:42
  • Glad I could help :-) – s7amuser Jun 08 '16 at 16:42

1 Answers1

1

Thanks to s7amuser for pointing out the bug. Below is the implementation that works for both even & odd cases.

/*
 * @brief   Checks if a list is a palindrome or not
 */
bool is_palindrome(node *head)
{
    node *first;                /* Points to head node of 1st half */
    node *second;               /* Points to head node of 2nd half */
    node *f_ptr = head;         /* Fast pointer */
    node *s_ptr = head;         /* Slow pointer */
    node *prev = NULL;          /* Previous to slow pointer */
    bool ret = false;           /* Return value */

    while (f_ptr && f_ptr->next && f_ptr->next->next)
    {
        prev = s_ptr;
        s_ptr = s_ptr->next;
        f_ptr = f_ptr->next->next;
    }

    /* List with odd number of elements */
    if (!(f_ptr->next))
    {
        first = head;
        second = s_ptr->next;
        prev->next = NULL;
        s_ptr->next = NULL;
        /* Reverse the second half */
        second = reverse_list(&second);
        /* Compare the first & second half */
        ret = are_identical(first, second);
        /* Finally, construct the original list back */
        second = reverse_list(&second);
        prev->next = s_ptr; s_ptr->next = second;
        print_list(head);
    }
    /* List with even number of elements */
    else if (!(f_ptr->next->next))
    {
        first = head;
        second = s_ptr->next;
        s_ptr->next = NULL;
        /* Reverse the second half */
        second = reverse_list(&second);
        /* Compare the first & second half */
        ret = are_identical(first, second);
        /* Finally, construct the original list back */
        second = reverse_list(&second);
        s_ptr->next = second;
        print_list(head);
    }
    return ret;
}
Community
  • 1
  • 1
rurtle
  • 411
  • 4
  • 18