Head продолжает устанавливаться на NULL

Я составил список с двойной связью и несколько раз запускал его с функциями для добавления узлов в конце, вывода длины списка и вывода всех элементов. Все работало нормально. Затем я создал функцию для добавления узла в определенное место под названием AddNodeAt(). и с тех пор он больше не работает. head продолжает устанавливаться на NULL. После того, как AddNode() выполнено, другие функции работают так, как будто head указывает на NULL, а не на список.

Это полный код:

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

struct node {
    node *prev;
    int val;
    node *next;
};
typedef struct node node;

node *head;

node *AddNode(node *head, int value)
{
    node *ptr = (node *)malloc(sizeof(node));

    if (!ptr) {
        printf("No Memory Available");
    } else
    if (!head) {
        ptr->val = value;
        ptr->next = NULL;
        ptr->prev = NULL;
        head = ptr;
    } else {
        node *temp = head;
        while (temp->next) {
            temp = temp->next;
        }
        temp->next = ptr;
        ptr->prev = temp;
        ptr->next = NULL;
        ptr->val = value;
    }
    return head;
}

int ListLength(node *head)
{
    int count = 0;
 
    if (head == NULL) {
        printf("\nEmpty List\n");
    } else {
        node *temp = head;
        count = 1;
        while (temp->next != NULL) {
            temp = temp->next;
            count++;
        }
        printf("\n%d nodes\n", count);
    }
    return count;
}

node *AddNodeAt(node *head, int position, int value)
{
    int i = 0;
    node *temp = head;
    node *ptr = (node *)malloc(sizeof(node));
    if (head == NULL && position != 1) {
        printf("Empty Linklist, cannot insert node at the given position");
    } else
    if (position > ListLength(head) + 1) {
        printf("list too short, cannot insert node at the given position");
    } else
    if (position == ListLength(head) + 1) {
        while (temp->next) {
            temp = temp->next;
        }
        temp->next = ptr;
        ptr->next = NULL;
        ptr->val = value;
        ptr->prev = temp;
    } else {
        while (i < position) {
            i++;
            temp = temp->next;
        }
        ptr->next = temp->next;
        temp->next = ptr;
        ptr->prev = temp;
        ptr->val = value;
        temp = ptr->next;
        temp->prev = ptr;
    }
    return head;
}

void PrintList(node *head)
{
    node *temp = head;
    if (!head) {
        printf("list empty");
    } else {
        while (temp->next != NULL) {
            printf("%d->", temp->val);
            temp = temp->next;
        }
        printf("%d", temp->val);
    }
}

int main() {
    int value = 0;
    int num = 0;
    int position = 0;
    printf("Number of nodes to be made: ");
    scanf("%d", &num);
    for (int i = 1; i <= num; i++) {
        printf("value in node number %d: ", i);
        scanf("%d", &value);
        AddNode(head, value);
    }
    PrintList(head);
    ListLength(head);

    printf("\nPosition of new node: ");
    scanf("%d", &position);
    printf("\nNumber of nodes:");
    scanf("%d", &value);
    AddNodeAt(head, position, value);

    PrintList(head);
    ListLength(head);

    return 0;
}

Любые рекомендации будут оценены по достоинству. Я попытался закомментировать разделы, которые добавил прямо перед тем, как возникла эта проблема. Я ожидал, что другие начнут работать так же, как работали раньше, но этого не произошло. head продолжает устанавливаться на NULL.

Эти объявления struct node { node *prev; интервал значений; узел *следующий; };typedef struct node node; не правы. В объявлении структуры имя узла не определено. Код можно скомпилировать, только если вы используете компилятор C++ вместо компилятора C.

Vlad from Moscow 04.06.2024 17:36
AddNode и AddNodeAt возвращают возможно измененную head, поэтому main() следует присвоить возвращаемое значение из этих функций head. Альтернативно, функции можно переписать, чтобы использовать двойной указатель на head (node **head).
Ian Abbott 04.06.2024 17:41

Ошибка №1: использование глобальной переменной. Не используйте глобальные переменные.

n. m. could be an AI 04.06.2024 17:43

... или скомпилируйте с помощью Clang и опции «-Weverything»: clang -Weverything file.c, чтобы увидеть все возможные предупреждающие сообщения, включая «объявление затеняет переменную в глобальной области видимости».

i486 05.06.2024 09:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
112
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Проблема в присвоении head=ptr в функции AddNode. Это изменит только локальную переменную head. Он не будет изменять глобальную переменную с тем же именем, эта глобальная переменная останется NULL.

Простое решение этой проблемы — использовать указатель, который возвращает AddNode:

head = AddNode(head,value);

У вас та же проблема с функцией AddNodeAt.


Кстати, не используйте глобальные переменные. Вы можете определить head локально внутри функции main.

Проблема была устранена, если сделать head локальной переменной в main и обновить ее с помощью возврата aAddNode(). Спасибо.

Abhinav Sharma 23.06.2024 15:29
Ответ принят как подходящий

head не устанавливается в NULL: глобальная переменная head инициализируется значением NULL перед запуском программы и никогда не обновляется, чтобы указывать на выделенный список.

Вам следует создать head локальную переменную в main и обновить ее до возвращаемого значения функций AddNode и AddNodeAt.

Обратите внимание на другие проблемы:

  • всегда проверяйте возвращаемое значение scanf() и сообщайте об ошибках ввода.
  • всегда проверяйте наличие ошибок распределения и сообщайте об ошибках.
  • используйте вспомогательные функции, чтобы сделать это простым и систематичным.
  • переместите typedef перед определением struct node, чтобы node было доступно для определений членов. Как указано, ваш код не компилируется как C, но является допустимым C++.
  • ListLength не должен ничего выводить. Используйте отдельные функции для вычисления длины списка и для печати длины списка.
#include <stdio.h>
#include <stdlib.h>

typedef struct node node;
struct node {
    node *prev;
    int val;
    node *next;
};

int get_integer(void) {
    for (;;) {
        int value;
        int c;
        switch (scanf("%d", &value)) {
          case 1:
            /* read and discard the rest of the input line */
            while ((c = getchar()) != EOF && c != '\n')
                continue;
            printf("\n");
            return value;
          case EOF:
            fprintf(stderr, "unexpected end of file, aborting\n");
            exit(1);
          default:
            /* read and discard the rest of the input line */
            while ((c = getchar()) != EOF && c != '\n')
                continue;
            fprintf(stderr, "invalid input, try again\n");
            break;
        }
    }
}

node *AddNode(node *head, int value) {
    node *ptr = (node *)malloc(sizeof(node));

    if (!ptr) {
        fprintf(stderr, "No Memory Available\n");
        exit(1);
    }
    ptr->val = value;
    ptr->next = NULL;
    ptr->prev = NULL;

    if (!head) {
        head = ptr;
    } else {
        node *temp = head;
        while (temp->next) {
            temp = temp->next;
        }
        temp->next = ptr;
        ptr->prev = temp;
    }
    return head;
}

int ListLength(const node *head) {
    int count = 0;
 
    while (head != NULL) {
        count++;
        head = head->next;
    }
    return count;
}

void PrintListLength(const node *head)  {
    if (head == NULL) {
        printf("Empty List\n");
    } else {
        printf("%d nodes\n", ListLength(head));
    }
}

node *AddNodeAt(node *head, int position, int value) {

    if (position > ListLength(head) + 1) {
        printf("list too short, cannot insert node at the given position\n");
        return head;
    }

    node *ptr = (node *)malloc(sizeof(node));
    if (!ptr) {
        fprintf(stderr, "No Memory Available\n");
        exit(1);
    }
    /* initialize the new node */
    ptr->value = value;
    ptr->next = NULL;
    ptr->prev = NULL;

    if (position <= 1) {
        /* insert at front */
        if (head) {
            ptr->next = head;
            head->prev = ptr;
        }
        head = ptr;
    } else {
        node *temp = head;

        for (int i = 1; i < position; i++) {
            temp = temp->next;
            i++;
        }
        ptr->prev = temp;
        temp->next = ptr;
        ptr->next = temp->next;
        if (ptr->next) {
            ptr->next->prev = ptr;
        }
    }
    return head;
}

void PrintList(const node *head) {
    const node *temp = head;
    if (!head) {
        printf("list empty\n");
    } else {
        while (temp->next != NULL) {
            printf("%d->", temp->val);
            temp = temp->next;
        }
        printf("%d\n", temp->val);
    }
}

void FreeList(node *head) {
    while (head) {
        node *next = head->next;
        free(head);
        head = next;
    }
}
        
int main(void) {
    int value = 0;
    int num = 0;
    int position = 0;
    node *head = NULL;

    printf("Number of nodes to be made: ");
    num = get_integer();

    for (int i = 1; i <= num; i++) {
        printf("value in node number %d: ", i);
        value = get_integer();
        head = AddNode(head, value);
    }
    PrintList(head);
    PrintListLength(head);

    printf("Position of new node: ");
    position = get_integer();
    printf("Node value: ");
    value = get_integer();
    head = AddNodeAt(head, position, value);

    PrintList(head);
    PrintListLength(head);

    FreeList(head);
    return 0;
}

Я использую код VS. Я не уверен, есть ли у него отдельный компилятор для C++ и C, поскольку я новичок в программировании. Я просто проверяю, что расширение файла «.c».

Abhinav Sharma 23.06.2024 15:16

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

    for (int i = 1; i <= num; i++)
        {
            printf("value in node nummber %d : ", i);
            scanf("%d", &value);
            AddNode(head, value);
        }
    PrintList(head);
    ListLength(head);

    printf("\nPosition of new node:");
    scanf("%d", &position);
    printf("\nNumber of node :");
    scanf("%d", &value);
    AddNodeAt(head, position, value);

И возвращаемое значение игнорируется. Если вы измените его на

    for (int i = 1; i <= num; i++)
        {
            printf("value in node nummber %d : ", i);
            scanf("%d", &value);
            head = AddNode(head, value);
        }
    PrintList(head);
    ListLength(head);

    printf("\nPosition of new node:");
    scanf("%d", &position);
    printf("\nNumber of node :");
    scanf("%d", &value);
    head = AddNodeAt(head, position, value);

Это будет работать. Но есть и другие ошибки.

Также вам необходимо изменить

struct node
{
    node* prev;
    int val;
    node* next;
};

к

struct node
{
    struct node* prev;
    int val;
    struct node* next;
};

Чтобы скомпилировать это в C, или хотя бы поместите typedef перед объявлением struct, как

typedef struct node node;
struct node
{
    node* prev;
    int val;
    node* next;
};

подробнее о коде

Не буду углубляться в ошибки. Вместо этого я предоставлю некоторое обсуждение и добавлю полный пример (и еще несколько обсуждений) в конце. Это длинный пост, и вы можете сразу перейти к TL;DR или просто проигнорировать все это...

AddNode()

Внутри AddNodehead находится локальный node*, но чуть выше мы видим node как глобальную node* вещь. Совсем не хорошо. А в main возвращаемое значение просто игнорируется.

node* head;

node* AddNode(node* head, int value)
{
    node* ptr = (node*)malloc(sizeof(node));

    if (!ptr)
        {
            printf("No Memory Available");
        }
    else if (!head)
        {
            ptr->val  = value;
            ptr->next = NULL;
            ptr->prev = NULL;
            head      = ptr;
        }
    else
        {
            node* temp = head;
            while (temp->next)
                {
                    temp = temp->next;
                }
            temp->next = ptr;
            ptr->prev  = temp;
            ptr->next  = NULL;
            ptr->val   = value;
        }
    return head;
}

Вы можете просто удалить этот аргумент или просто начать использовать возвращаемое значение для обновления заголовка списка.

node* AddNodeAt(node* head, int position, int value)

В этом тоже есть ошибки.

Например, если это условие истинно:

    node* ptr  = (node*)malloc(sizeof(node));
    if (head == NULL && position != 1)
    {
        printf(
            "Empty Linklist,cant insert node at the "
            "given position");
    }

функция напечатает сообщение и вернет head без изменений. Но выделенная память в ptr пропала навсегда...

Также не работает position — это 1, где нужно добавить узел в заголовке.

В любом случае, поскольку значение, возвращаемое функцией, просто игнорируется в main, у списка все равно нет будущего.

подробнее о коде

  • использовать прототипы. main должна быть первой функцией в файле кода. Или если можно в отдельном файле. Таким образом, вы сможете написать множество небольших тестовых программ и протестировать все быстрее.
  • не пишите интерактивный код. Это только замедлит вас. Много. Используйте файлы, константы, фабричные функции, генераторы, что угодно, кроме запроса пользователя на количество элементов, а затем на элементы.
  • scanf возвращается и int не просто так. прочитайте руководство. Сравнивает возвращаемое значение с количеством ожидаемых элементов. scanf это --- отсюда и название --- сканер, и нормально ничего не читает. Что касается вашей программы... Просто введите e вместо 4 чуть выше и бац: вот и ваша программа.
  • никогда не используйте глобальные переменные, например head в вашем случае. Это катастрофа. Используйте аргументы.

подробнее о данных

связанные списки являются контейнерами. Все списки равны: просто --- возможно, пустая --- последовательность узлов. Не данные, просто узлы. Каждый узел содержит (ваш случай) или указывает на одну единицу данных, назовем ее записью. Таким образом, операции в списке не зависят от того, что указано в данный момент. Или не должен. Таким образом, ваш код можно будет повторно использовать для следующих классических упражнений, таких как список тиков фильмов, список воспроизведения музыки, список книг библиотеки, список управления парковкой :D и все другие обычные подозреваемые.

list в предоставленном коде

typedef struct node node;
struct node
{
    node* prev;
    int val;
    node* next;
};

node* head;

Это просто узел и глобальный указатель на узел. В списке нет даже упоминания.

TL;DR ПРИМЕР

возможная list альтернатива

// this is a node
typedef struct st_node
{
    Info*           info;
    struct st_node* prev;  // links
    struct st_node* next;
} Node;

// this is a list
typedef struct
{
    size_t size;
    Node*  head;
    Node*  tail;
} List;

Теперь у нас есть связанный список. И в списке есть метаданные: ожидаемый минимум: указатели на заголовок и хвост и счетчик размера. И вы видите, что жизнь может быть проще: у каждого List есть своя head, tail и size вещь.

А что с узлом? Каждый узел имеет ссылки и указатель на какую-то вещь. Таким образом, вы можете иметь списки чего угодно: сами данные даже не упоминаются в узле, кроме имени.

Вы можете объявить

    List one;
    List other = { 0, NULL, NULL };
    List* set[30];

и иметь списки Info и one и массив из 30 указателей на списки от other до set[0].

set[29] операции

List* so_create_list(void);
List* so_delete_list(List*);
int   so_insert(Info*, List*, int (*compare)(void*, void*));
int   so_insert_at(int position, Info* value, List* list);
int   so_insert_back(Info*, List*);
int   so_insert_front(Info*, List*);
int   so_print_list(FILE*,List*,const char*);
int   so_sort_nodes(List*, int (*compare)(void*, void*));

Написав таким образом, мы видим, что узлы являются внутренними по отношению к списку и не присутствуют ни в одном списке аргументов. Мы используем только указатели на List и List, поэтому, если список изменится или даже если Info изменится полностью, от Info в этом примере до сложной динамически выделяемой многоуровневой структуры, это не будет иметь никакого значения для самого списка.

Фактически, если каждый узел содержит указатель на запись данных, все, что нужно знать списку, — это то, как

  • скопируйте запись, чтобы список не зависел от данных или указателей, не владеющих (предоставленных пользователем). Например, в int это называется конструктором копирования.
  • удалить запись, поскольку для записи может быть выделена память. В C++ это называется деструктором.
  • показать запись на экране, так как есть функция печати списка, а список ничего не знает о записях. В Java это называется методом C++.
  • сравнить две записи, если нам может понадобиться отсортировать список или вставить записи в каком-то порядке. Это похоже на функцию сравнения в функции toStringC.

Если мы предоставим это в список, это может быть список чего угодно. Универсальный и полезный. Я не буду сейчас это реализовывать, но надеюсь, что вы поймете идею из кода: это всего лишь вопрос написания этих узловых операций как функций и использования их в нужных местах.

qsort

Эта функция встречается в основном в студенческих заданиях. Не имеет особого смысла обращаться к данным в связанных списках по позиции. Если это важно, мы, вероятно, будем использовать массивы или хеш-таблицы. Связанные списки созданы для навигации по ссылкам.

В любом случае, возможная реализация приведена в примере кода, как и в некоторых случаях использования.

создание простого примера на основе исходного кода

Ниже приведен пример использования insert_at(int position, int value, List* list); внутри узла в качестве данных.

int: список функций для примера

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

// these are operations on data
int so_cmp_info(void* a, void* b);
int so_print_info(int, const char*);

// this is a node
typedef struct st_node
{
    int             info;
    struct st_node* prev;  // links
    struct st_node* next;
} Node;

// this is a list
typedef struct
{
    size_t size;
    Node*  head;
    Node*  tail;
} List;

// operations on List
List* so_create_list(void);
List* so_delete_list(List*);
int   so_insert(int, List*, int (*compare)(void*, void*));
int   so_insert_at(int position, int value, List* list);
int   so_insert_back(int, List*);
int   so_insert_front(int, List*);
int   so_print_list(List*, const char*);
int   so_print_list_r(List*, const char*);
int   so_sort_nodes(List*, int (*compare)(void*, void*));

Поскольку list.h не является распространенной операцией для связанных списков, so_insert_at() также не является распространенной операцией. Это здесь просто для того, чтобы показать, что все, что нам нужно для сортировки, это:

  • функция сравнения
  • возможность навигации по элементам, с чем связанные списки справляются очень хорошо. В любом случае, исходный код ниже.

Наличие функции сравнения 2-х записей облегчает написание

int   so_insert(int, List*,int (*compare)(void*, void*));

для вставки записей в определенном порядке, например ISBN для списка книг или продолжительность песни для списков воспроизведения. У нас может быть много функций сравнения с разными критериями. Если мы изменим критерии, необходимо отсортировать список с использованием новых критериев перед вставкой с использованием новой функции сравнения, и это причина использования функции сортировки.

Возможная реализация so_sort_nodes() приведена в примере.

Несколько примеров:

List* so_create_list(void)
{
    List* list = malloc(sizeof(List));
    if (list == NULL) return NULL;
    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
    fprintf(stderr, "list created\n");
    return list;
}

Это возможный способ создания списка. Использовать его так же просто, как в примере:

        List* list = so_create_list();

so_insert возвращается в случае ошибки.

// inserts 'info' at the tail of the list
int so_insert_back(int info, List* list)
{
    if (list == NULL) return -1;  // no list
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -2;  // no alloc

    // copy the record pointed by 'info'
    node->info = info;

    if (list->size == 0)  // first node
    {
        node->next = NULL;
        node->prev = NULL;
        list->size = 1;
        list->head = node;
        list->tail = node;
        return 0;  // 1st node
    };
    node->next       = NULL;
    node->prev       = list->tail;
    list->tail->next = node;
    list->tail       = node;
    list->size += 1;
    return 0;
}

Это простая реализация NULL, которая вставляет запись в конец списка. insert_back() аналогичен, поскольку в списке имеются указатели на голову и хвост.

    List* list = so_create_list();
    if (list == NULL) return -1;
    for (int info = 1; info <= 10; info += 2)
        so_insert_back(info, list);
    so_print_list(list, "\tinserted 1 to 9 ==>\n\n");
    for (int info = -1; info >= -10; info -= 2)
        so_insert_front(info, list);
    so_print_list(list, "\tinserted -1 to -9 ==>\n\n");

В этих строках из примера некоторые записи вставляются в начало и конец списка и распечатываются. Выход:

list created

        inserted 1 to 9 ==>

5 data points:
First: 1    Last: 9
   1    3    5    7    9


        inserted -1 to -9 ==>

10 data points:
First: -9    Last: 9
  -9   -7   -5   -3   -1    1    3    5    7    9

И мы видим удобство неинтерактивного кода и возможность принятия дополнительного заголовка в insert_front()

В качестве примера упорядочения эти строки в примере

    so_sort_nodes(list, so_cmp_info);
    so_print_list(list, "\tsorted descending value ==>\n\n");

это все, что нам нужно, чтобы отсортировать список на месте в порядке убывания --- в любом случае на основе функции сравнения.

so_print_list() пример реализации этих функций

#include "List.h"

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

Node* so_locate_at(List* list, size_t pos);
int   so_swap_info(Node* one, Node* other);

// list related functions

List* so_create_list(void)
{
    List* list = malloc(sizeof(List));
    if (list == NULL) return NULL;
    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
    fprintf(stderr, "list created\n");
    return list;
}

List* so_delete_list(List* del)
{
    if (del == NULL) return NULL;  // no list
    Node* save = NULL;
    for (size_t i = 0; i < del->size; i += 1)
    {  // delete nodes, 1 by 1
        save = del->head->next;
        // delete the record pointed by 'head'
        free(del->head);
        del->head = save;
    };
    free(del);  // delete list
    fprintf(stderr, "list deleted\n");
    return NULL;
}

int so_insert(
    int value, List* list, int (*cmp)(void*, void*))
{
    if (list == NULL) return -1;  // no list
    Node* p = list->head;
    for (size_t i = 0; i < list->size; i += 1)
    {   // cmp() > 0 marks the insertion point
        if (cmp(&value, &p->info) > 0)
        {
            Node* node = (Node*)malloc(sizeof(Node));
            if (node == NULL) return -4;  // no alloc
            node->info = value;
            // link the new node before 'p'
            if (p == list->head)
                return so_insert_front(value, list);
            p->prev->next = node;
            node->next    = p;
            node->prev    = p->prev;
            p->prev       = node;
            // update size
            list->size += 1;
            return 0;
        }
        p = p->next;
    }
    // all nodes ordered. insert at the end
    return so_insert_back(value, list);
}

// inserts 'val' at 'position' in 'List'
// first position is 1
int so_insert_at(int position, int val, List* list)
{
    if (list == NULL) return -1;
    // insert at head?
    if (position == 1) return so_insert_front(val, list);
    if (list->size == 0) return -2;  // empty list
    if (position > list->size) return -3;
    if (position == list->size)
        return so_insert_back(val, list);
    // ok: just advance pointer 'position'-1 times
    Node* p = list->head;
    for (int i = 1; i < position; i += 1) p = p->next;
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -4;  // no alloc
    node->info = val;
    // link the new node before 'p'
    p->prev->next = node;
    node->next    = p;
    node->prev    = p->prev;
    p->prev       = node;
    // update size
    list->size += 1;
    return 0;
}

// inserts 'info' at the tail of the list
int so_insert_back(int info, List* list)
{
    if (list == NULL) return -1;  // no list
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -2;  // no alloc

    // copy the record pointed by 'info'
    node->info = info;

    if (list->size == 0)  // first node
    {
        node->next = NULL;
        node->prev = NULL;
        list->size = 1;
        list->head = node;
        list->tail = node;
        return 0;  // 1st node
    };
    node->next       = NULL;
    node->prev       = list->tail;
    list->tail->next = node;
    list->tail       = node;
    list->size += 1;
    return 0;
}

// inserts 'info' at the head of the list
int so_insert_front(int info, List* list)
{
    if (list == NULL) return -1;  // no list
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -2;  // no alloc

    // copy the record pointed by 'info'
    node->info = info;

    if (list->size == 0)
    {
        node->next = NULL;
        node->prev = NULL;
        list->size = 1;
        list->head = node;
        list->tail = node;
        return 0;  // 1st node
    };
    node->next       = list->head;
    node->prev       = NULL;
    list->head->prev = node;
    list->head       = node;
    list->size += 1;
    return 0;
}

int so_print_list(List* list, const char* msg)
{
    FILE* out = stdout;
    if (list == NULL)
    {
        fprintf(out, "No valid list!\n");
        return 0;
    }
    if (list->size == 0)
    {
        fprintf(out, "list is empty\n");
        return 0;
    }
    if (msg != NULL)
        printf(
            "\n%s%zd data points:\nFirst: %d    Last: %d\n",
            msg, list->size, list->head->info,
            list->tail->info);
    else
        printf(
            "\n%zd data points:\nFirst: %d    Last: %d\n",
            list->size, list->head->info, list->tail->info);
    Node* p = list->head;
    for (size_t i = 0; i < list->size; i += 1)
    {
        so_print_info(p->info, NULL);
        p = p->next;
    }
    printf("\n\n");
    return 0;
}

int so_print_list_r(List* list, const char* msg)
{
    FILE* out = stdout;
    if (list == NULL)
    {
        fprintf(out, "No valid list!\n");
        return 0;
    }
    if (list->size == 0)
    {
        fprintf(out, "list is empty\n");
        return 0;
    }
    if (msg != NULL)
        printf(
            "\n%s%zd data points (reverse order):\nFirst: "
            "%d    Last: %d\n",
            msg, list->size, list->head->info,
            list->tail->info);
    else
        printf(
            "\n%zd data points (reverse order):\nFirst: %d "
            "   Last: %d\n",
            list->size, list->head->info, list->tail->info);

    Node* p = list->tail;
    for (size_t i = 0; i < list->size; i += 1)
    {
        so_print_info(p->info, NULL);
        p = p->prev;
    }
    printf("\n[-----]\n\n");
    return 0;
}

// return a pointer to a copy of the record
// at such 'pos', or 'NULL'. 'pos'starts at 0
Node* so_locate_at(List* list, size_t pos)
{
    if (list == NULL) return NULL;
    if (list->size < 1 + pos) return NULL;
    Node* nd = list->head;
    for (size_t i = 0; i < pos; i += 1) nd = nd->next;
    return nd;
}

int so_sort_nodes(List* list, int (*cmp)(void*, void*))
{
    if (list == NULL) return -1;
    if (list->size < 2) return -2;
    if (cmp == NULL) return -3;
    for (int i = 0; i < list->size - 1; i += 1)
        for (int j = 0; j < list->size - i - 1; j += 1)
        {
            Node* one = so_locate_at(list, j);
            if (cmp((void*)one, (void*)one->next) < 0)
                so_swap_info(one, one->next);
        }
    return 0;
}

// swap data from these 2 nodes
int so_swap_info(Node* one, Node* other)
{
    if (one == NULL) return -1;
    if (other == NULL) return -1;
    int temp    = one->info;
    one->info   = other->info;
    other->info = temp;
    return 0;
}

int so_cmp_info(void* a, void* b)
{
    if (a == NULL) return 0;
    if (b == NULL) return 0;
    int ta = ((Node*)a)->info;
    int tb = ((Node*)b)->info;

    if (ta > tb) return +1;
    if (ta < tb) return -1;

    return 0;
};

int so_print_info(int info, const char* msg)
{
    if (msg != NULL) printf("%s", msg);
    printf("%4d ", info);
    return 0;
}

list.c для примера

Этот код создает список и тестирует некоторые функции. Список распечатывается на каждом этапе. В конце список сортируется, отображается и удаляется.

#include <stdio.h>
#include <stdlib.h>
#include "list.h"
int main(void)
{
    List* list = so_create_list();
    if (list == NULL) return -1;
    for (int info = 1; info <= 10; info += 2)
        so_insert_back(info, list);
    so_print_list(list, "\tinserted 1 to 9 ==>\n\n");
    for (int info = -1; info >= -10; info -= 2)
        so_insert_front(info, list);
    so_print_list(list, "\tinserted -1 to -9 ==>\n\n");
    // insert at the tail using so_insert_at()
    so_insert_at((int)list->size, 10, list);
    so_insert_at((int)list->size, 11, list);
    so_print_list(
        list, "\tinserted 10 & 11 at the end ==>\n\n");
    // inser at head  using so_insert_at()
    so_insert_at(1, -10, list);
    so_insert_at(1, -11, list);
    so_print_list(
        list, "\tinserted -10 & -11 at the head ==>\n\n");

    so_insert_at(9,2, list);
    so_print_list(list, "\tinserted 2 at pos 9 ==>\n\n");
    so_insert_at(11,4, list);
    so_print_list(list, "\tinserted 4 at pos 11 ==>\n\n");
    so_insert_at(13,6, list);
    so_print_list(list, "\tinserted 6 at pos 13 ==>\n\n");

    so_sort_nodes(list, so_cmp_info);
    so_print_list(list, "\tsorted descending value ==>\n\n");
    so_delete_list(list);
    return 0;
}

вывод для примера

list created

        inserted 1 to 9 ==>

5 data points:
First: 1    Last: 9
   1    3    5    7    9


        inserted -1 to -9 ==>

10 data points:
First: -9    Last: 9
  -9   -7   -5   -3   -1    1    3    5    7    9


        inserted 10 & 11 at the end ==>

12 data points:
First: -9    Last: 11
  -9   -7   -5   -3   -1    1    3    5    7    9   10   11


        inserted -10 & -11 at the head ==>

14 data points:
First: -11    Last: 11
 -11  -10   -9   -7   -5   -3   -1    1    3    5    7    9   10   11


        inserted 2 at pos 9 ==>

15 data points:
First: -11    Last: 11
 -11  -10   -9   -7   -5   -3   -1    1    2    3    5    7    9   10   11


        inserted 4 at pos 11 ==>

16 data points:
First: -11    Last: 11
 -11  -10   -9   -7   -5   -3   -1    1    2    3    4    5    7    9   10   11


        inserted 6 at pos 13 ==>

17 data points:
First: -11    Last: 11
 -11  -10   -9   -7   -5   -3   -1    1    2    3    4    5    6    7    9   10   11


        sorted descending value ==>

17 data points:
First: 11    Last: -11
  11   10    9    7    6    5    4    3    2    1   -1   -3   -5   -7   -9  -10  -11

list deleted

вставка узлов по порядку

Этот небольшой пример показывает, как вставлять данные, упорядоченные по некоторым критериям:

#include <stdio.h>
#include <stdlib.h>
#include "list.h"
int main(void)
{
    srand(240605);
    List* list = so_create_list();
    if (list == NULL) return -1;
    for (int i = 0; i < 20; i += 1)
        so_insert(rand()%100, list, so_cmp_info);
    so_print_list(list, "\tinserted 20 elements with values from [0..99] ==>\n");
    // force insert at the end
    so_insert(1000, list, so_cmp_info);
    so_print_list(list, "\tinserted 1000 ==>\n");
    // force insert at the head
    so_insert(-1000, list, so_cmp_info);
    so_print_list(list, "\tinserted -1000 ==>\n");
    so_delete_list(list);
    return 0;
}

выход

list created

        inserted 20 elements with values from [0..99] ==>
20 data points:
First: 99    Last: 3
  99   94   91   89   86   85   84   83   79   79   72   62   61   42   37   36   35   22   12    3


        inserted 1000 ==>
21 data points:
First: 1000    Last: 3
1000   99   94   91   89   86   85   84   83   79   79   72   62   61   42   37   36   35   22   12    3


        inserted -1000 ==>
22 data points:
First: 1000    Last: -1000
1000   99   94   91   89   86   85   84   83   79   79   72   62   61   42   37   36   35   22   12    3 -1000

list deleted

Это немного не по теме, здесь только для полноты картины.

Использование возврата к обновлению головы сработало и устранило проблему. Спасибо. Я также переместил main наверх и объявил функции вверху. Это было очень полезно и помогло мне решить аналогичную проблему в коде двоичного дерева.

Abhinav Sharma 23.06.2024 15:32

Другие вопросы по теме

Зачем предпочитать DoubleLinkedList вместо очереди и хэш-карты для разработки наименее недавно используемого (LRU)?
AttributeError: объект «NoneType» не имеет атрибута «следующий»; просмотрел несколько статей с одной и той же ошибкой
Нужно ли удалять все ссылки на объект с помощью std::shared_ptr
Можно ли найти количество элементов в циклическом двусвязном списке?
Как сделать пузырьковую сортировку в двусвязном списке?
Создайте электронную таблицу из списка кортежей (строка, столбец, значение), используя двусвязный список (связанные списки связанных списков)
Почему один вариант обмена узлами моего двусвязного списка работает, а другой почти идентичный вариант не работает на соседних узлах?
Является ли эта сортировка сортировкой вставками?
Создайте функцию для добавления двусвязного списка к себе в конце
Не удается удалить узел из списка узлов