Круговой связанный список аварийно завершает работу при отображении

Я пытаюсь сделать круговой связанный список. Когда я пытаюсь отобразить список после его создания, программа продолжает падать. Вот мой код:

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

typedef struct node {
    int data;
    struct node * next;
} node;

node * createList(int);
void display(node * head);

int main() {
    struct node * head;

    head = createList(5);
    display(head);

}

node * createList(int n) {

    int i = 0,data = 0;
    struct node * head = NULL;
    struct node * temp = NULL;
    struct node * p = NULL;

    for (i = 0; i < n; i++) {
        temp = (node*)malloc(sizeof(node));
        temp->data = data++;
        temp->next = head;

        if (head == NULL) {
            head = temp;
        } else {
            p = head;
            while (p->next != NULL) {
                p = p->next;
            }
            p->next = temp;
        }
    }
    return head;
}

void display(node * head) {
    struct node * temp = head->next;
    while (temp != head) {
        printf("%d-> \t",temp->data);
        temp = temp->next;
    }
    printf("\n");
}

Что я делаю не так?


person VenoM    schedule 18.04.2021    source источник
comment
Подсказка: объясните эту строку кода: temp->next = head; Какова ее цель? (Это приводит к бесконечному зацикливанию while (p->next != NULL) {.)   -  person chux - Reinstate Monica    schedule 18.04.2021
comment
У вас не будет while (p->next != NULL) в циклическом списке --- указатель last node->next указывает на головной узел (отсюда циклический список)   -  person David C. Rankin    schedule 18.04.2021


Ответы (2)


  1. Вы установили для каждого temp next значение head в temp->next = head;, но сделали это слишком рано (первый — просто NULL). Затем вы протестировали p->next против NULL в while (p->next != NULL) {, но вы должны были протестировать против head. В качестве альтернативы вы можете продолжить тестирование против NULL, но тогда вам нужно инициализировать temp->next в NULL и назначить head в temp->next только после цикла for.

  2. Ваш отображаемый код начался со второй ссылки.

Вот фиксированный код, использующий первый вариант в 1. выше:

    for (i = 0; i < n; i++) {
        temp = (node*)malloc(sizeof(node));
        temp->data = data++;

        if (head == NULL) {
            head = temp;
        } else {
            p = head;
            while (p->next != head) {
                p = p->next;
            }
            p->next = temp;
        }
        temp->next = head;
    }

Вот фиксированный код, использующий альтернативный вариант в 1. выше. Вам все еще нужно инициализировать temp->next до NULL, так как malloc() не инициализируется.

    for (i = 0; i < n; i++) {
        temp = (node*)malloc(sizeof(node));
        temp->data = data++;
        temp->next = NULL;

        if (head == NULL) {
            head = temp;
        } else {
            p = head;
            while (p->next != NULL) {
                p = p->next;
            }
            p->next = temp;
        }
    }
    if (temp != NULL) {
        temp->next = head;
    }

Но на самом деле не нужно ходить с головой на каждое творение. Вы можете просто сохранить предыдущее и связать его со следующим:

    for (i = 0; i < n; i++) {
        temp = (node*)malloc(sizeof(node));
        temp->data = data++;

        if (head == NULL) {
            head = p = temp;
        } else {
            p = p->next = temp;
        }
    }
    if (temp != NULL) {
        temp->next = head;
    }

Вот исправление для display():

void display(node * head) {
    struct node * temp = head;
    if (temp != NULL) {
        do {
            printf("%d-> \t",temp->data);
            temp = temp->next;
        } while (temp != head);
    }
    printf("\n");
}
person niry    schedule 18.04.2021
comment
Спасибо за ответ, я попробовал ваш код, и он все еще падает :/ - person VenoM; 18.04.2021
comment
@VenoM Я также инициализировал temp->next в NULL, попробуйте сейчас - person niry; 18.04.2021
comment
Да, добавление temp-›next = NULL помогло, спасибо! - person VenoM; 18.04.2021

Проблема заключается в первом узле, который вы инициализируете:

    struct node *head = NULL;
    ...
    for (i = 0; i < n; i++) {
        ...
        temp->next = head;

Таким образом, tmp->next == NULL на первой итерации оставляет head->next == NULL. Это не будет работать для кругового списка. При попытке вставить второй узел:

            p = head;
            while (p->next != NULL) {

Что снова было head->next?? (о, NULL) Разыменование указателя NULL (BOOM Segfault!)

Делайте круговой список правильно. При вставке первого набора узлов:

        if (head == NULL) {
            head = temp;
            head->next = temp;              /* you must set head->next to temp */
        } ...

Итак, при вставке остальных узлов вам просто нужно:

        } else {
            p = head;
            while (p->next != head) {       /* iterate to last node */
                p = p->next;
            }
            p->next = temp;                 /* now set p->next = temp */
        }

Теперь вы обрабатываете свою функцию display() таким же образом, например.

void display (node *head)
{
    if (!head) {                            /* validate list not empty */
        puts ("(list-empty)");
        return;
    }
    
    struct node *temp = head;
    
    do {                                    /* same loop problem fixed in display() */
        printf ("%d-> \t", temp->data);
        temp = temp->next;
    } while (temp != head);
    
    putchar ('\n');
}

Если вы внесете изменения, вы можете проверить свой список с помощью:

int main (void) {
    struct node *head, *tmp;

    head = createList(5);
    display (head);

    puts ("\niterate from mid-list");
    tmp = head;
    tmp = tmp->next;
    tmp = tmp->next;
    display (tmp);
}

Пример использования/вывода

$ ./bin/lls_circular_fix
0->     1->     2->     3->     4->

iterate from mid-list
2->     3->     4->     0->     1->

Наконец, вы не умножаете тип node на head в struct node * head = NULL;. Запишите его как struct node *head = NULL; (то же самое и для всех ваших объявлений функций). Гораздо читабельнее.

Когда вы собираетесь удалить заметку из списка, вы должны создать особый случай как для head, так и для tail (последний узел). В этом смысле односвязный список требует немного больше усилий, чем двусвязный список, из-за отсутствия указателя узла prev для отслеживания предыдущего узла.

Просмотрите вещи и дайте мне знать, если у вас есть вопросы.

Полный пример:

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

typedef struct node {
    int data;
    struct node *next;
} node;

node *createList (int);
void display (node *head);

int main (void) {
    struct node *head, *tmp;

    head = createList(5);
    display (head);

    puts ("\niterate from mid-list");
    tmp = head;
    tmp = tmp->next;
    tmp = tmp->next;
    display (tmp);
}

node *createList (int n)
{
    int i = 0,data = 0;
    struct node *head = NULL;
    struct node *temp = NULL;
    struct node *p = NULL;

    for (i = 0; i < n; i++) {
        if (!(temp = malloc (sizeof *temp))) {
            perror ("malloc-temp");
            return NULL;
        }
        temp->data = data++;
        temp->next = head;                  /* head is NULL on 1st node insertion */

        if (head == NULL) {
            head = temp;
            head->next = temp;              /* you must set head->next to temp */
        } else {
            p = head;
            while (p->next != head) {       /* iterate to last node */
                p = p->next;
            }
            p->next = temp;                 /* now set p->next = temp */
        }
    }
    return head;
}

void display (node *head)
{
    if (!head) {                            /* validate list not empty */
        puts ("(list-empty)");
        return;
    }
    
    struct node *temp = head;
    
    do {                                    /* same loop problem fixed in display() */
        printf ("%d-> \t", temp->data);
        temp = temp->next;
    } while (temp != head);
    
    putchar ('\n');
}
person David C. Rankin    schedule 18.04.2021