Question

Middle node of a linked list

i am trying to return a middle node of linked-list, it works well when number of nodes are even and when number of nodes are odd, segmentation fault is all i see. initially i return slow if fast reached NULL, but it still fails for odd number of nodes. i tried two conditions: 1. if fast reached NULL AND length of linked-list is even: return slow 2. if fast reach NULL AND length of linked-list is odd: return slow->next. i also run gdb which points the seg fault to: fast = fast->next->next; in the middle_node(node *) function. Appreciate.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>


typedef struct node
{
    // holds number of type int
    int number;
    // holds pointer to node of the same type
    struct node *next;
} node;

int count = 0;    

// insert/tuck new node into the existing linked list
node *insert(node *, int);

// delete entire linked list
bool destroy(node *);

// return size of  linked-list
int list_size(node *);

// return middle node of linked list
node *middle_node(node *);

int main(void)
{
    // declare head node
    node *list = NULL;
    
    // inserting  into linked list with a loop var 
    for (int i = 0; i < 11; i++)
    {
        list = insert(list, 1 + i);    
    }

    node *middle = NULL;
    middle = middle_node(list);
    printf("node: %p, value: %i\n", middle, middle->number);
    
    // destroy/free heaps
    destroy(list);
 }


// insert/tuck new node into the existing linked list
node *insert(node *head, int x)
{
    // 1 dynamically allocate space for the new node
    node *new = (node *)malloc(sizeof(node));
    // 2 check to make sure we didnt run out of memory
    if (new == NULL)
    {
        return 0; // NULL
    }
    // 3 populate and insert the node at the beginning of the linked list
    new->number = x;
    // 3B arrange/connect the pointers in the correct order
    // connect new element's next pointer to head first
    new->next = head;
    // set head to point to new element
    head = new;
    // 4 return a pointer to the new head of linked list
    return head;
}

// delete/free entire singly linked list
bool destroy(node *head)
{
    // 1 if youve reach a NULL pointer, stop
    if (head != NULL)
    {
        // 2 else: delete the rest of the list (recursively)
        destroy(head->next);
        // 3 free the current node
        free(head);
        return true;
    }
    return false;
}

// return size of linked list
int list_size(node *head)
{
    // return size of linked list
    // temp points to head of list
    node *temp = head;
    int size = 0;
    while (temp != NULL)
    {
        size += 1;
        temp = temp->next;
    }
    return size;
}

// return middle node of linked list
node *middle_node(node *head)
{
    // declare to pointers fast and slow of type node *
    if (head != NULL)
    {
        node *slow = NULL, *fast = NULL, *current = NULL;
        slow = head;
        fast = head;
        current = head;
        while (current != NULL)
        {
            count++;
            // one step at a time
            slow = slow->next;
            // two step at a time
            fast = fast->next->next;            
            
            // when fast points to last node
            if ((fast == NULL) && (list_size(head) % 2 == 1))
            {
                // return slow as the middle node 
                return slow->next;
            }
            if ((fast == NULL) && (list_size(head) % 2 == 0))
            {
                return slow;
            }

            // else update current node one step forward
            current = current->next;
        }
    }
    else
    {
        // return NULL otherwise
        return NULL;
    }
}
 3  116  3
1 Jan 1970

Solution

 7

Your fast runner, fast = fast->next->next;, will try to dereference NULL in the cases the linked list contains an odd number of elements. Think about if the list contains one element only. fast->next will then be NULL and fast->next->next will then dereference NULL, which results in the program having undefined behavior.

You need to check that dereferencing fast->next returns non-NULL before going on to dereferencing fast->next->next. It could look like this:

node *middle_node(node *head) {
    node *slow = head;
    while(head && head->next) {
        slow = slow->next;
        head = head->next->next;
    }
    return slow;
}

Demo


You could make use of your list_size function to simplify this - and it goes through the list 1.5 times, just as your slow and fast runners do.

node *middle_node(node *head) {
    int size = list_size(head) / 2;
    while(size--) head = head->next;    
    return head;
}

Demo

2024-07-01
Ted Lyngmo