Как реализовать связанный список в C? - PullRequest
8 голосов
/ 11 июня 2009

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

Я реализовал инициализацию, добавление и clean_up. Однако я не уверен, что сделал это правильно.

Когда я добавляю продукт в список, я объявляю некоторую память с помощью calloc. Но я думаю, я не должен вместо этого объявлять память для продукта. Я действительно смущен этим добавлением.

Большое спасибо за любые предложения,

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

#define PRODUCT_NAME_LEN 128

typedef struct product_data 
{
    int product_code;
    char product_name[PRODUCT_NAME_LEN];
    int product_cost;
    struct product_data_t *next;
}product_data_t;

typedef struct list 
{
    product_data_t *head;
    product_data_t *tail;
}list_t;

void add(list_t *list, int code, char name[], int cost); 
void initialize(list_t *list);
void clean_up(list_t *list);

int main(void)
{
    list_t *list = NULL;

    initialize(list);
    add(list, 10, "Dell Inspiron", 1500);
    clean_up(list);

    getchar();

    return 0;
}

void add(list_t *list, int code, char name[], int cost)
{
    // Allocate memory for the new product
    list = calloc(1, sizeof(list_t));
    if(!list)
    {
        fprintf(stderr, "Cannot allocated memory");
        exit(1);
    }

    if(list)
    {
        // First item to add to the list
        list->head->product_code = code;
        list->head->product_cost = cost;
        strncpy(list->head->product_name, name, sizeof(list->head->product_name));
        // Terminate the string
        list->head->product_name[127] = '/0';
    } 
}

// Initialize linked list
void initialize(list_t *list)
{
    // Set list node to null
    list = NULL;
    list = NULL;
}

// Release all resources
void clean_up(list_t *list)
{    
    list_t *temp = NULL;

    while(list)
    {
        temp = list->head;
        list->head = list->head->next;
        free(temp);    
    }
    list = NULL;
    list = NULL;
    temp = NULL;
}

============================== Отредактировано ================ ============

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

#define PRODUCT_NAME_LEN 64

// typedef struct product_data product_data_t;
typedef struct product_data 
{
    int product_code;
    char product_name[PRODUCT_NAME_LEN];
    int product_cost;
}product_data_t;

typedef struct list
{
    struct list *head;
    struct list *tail;
    struct list *next;
    struct list *current_node;
    product_data_t *data;

}list_t;

void add(list_t *list, int code, char name[], int cost); 

int main(void)
{
    list_t *list = NULL;
    list = initialize(list);
    add(list, 1001, "Dell Inspiron 2.66", 1299);

    add(list, 1002, "Macbook Pro 2.66", 1499);

    clean_up(list);

    getchar();

    return 0;
}

void add(list_t *list, int code, char name[], int cost)
{
    /* Allocate memory for the new product */
    product_data_t *product = (product_data_t*) calloc(1, sizeof(*product));
    if(!product)
    {
        fprintf(stderr, "Cannot allocate memory.");
        exit(1);
    }

    /* This is the first item in the list */
    product->product_code = code;
    product->product_cost = cost;
    strncpy(product->product_name, name, sizeof(product->product_name));
    product->product_name[PRODUCT_NAME_LEN - 1] = '\0';

    if(!list->head)
    {
        /* Assign the address of the product. */
        list = (list_t*) product;   
        /* Set the head and tail to this product */
        list->head = (list_t*) product;
        list->tail = (list_t*) product;
    }
    else
    {
        /* Append to the tail of the list. */
        list->tail->next = (list_t*) product;
        list->tail = (list_t*) product;
    }

    /* Assign the address of the product to the data on the list. */
    list->data = (list_t*) product;
}

Ответы [ 14 ]

6 голосов
/ 11 июня 2009

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

Скажем, у вас есть:

struct Whatever
{
   int x_;
}

Тогда ваша структура списка будет выглядеть так:

struct Whatever_Node
{
   Whatever_Node* next_
   Whatever* data_
}

Райан Оберой прокомментировал аналогично, но без примера.

4 голосов
/ 11 июня 2009

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

typedef struct listnode
{
   //some data
   struct listnode *next;
}listnodeT;

listnodeT *list;
listnodeT *current_node;
list = (listnodeT*)malloc(sizeof(listnodeT));
current_node = list;

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

    // Initialize linked list
void initialize(list_t *list)
{
    list->head = NULL;
    list->tail = NULL;
}

void add(list_t *list, int code, char name[], int cost)
{
    // set up the new node
    product_data_t *node = (product_data_t*)malloc(sizeof(product_data_t));
    node->code = code;
    node->cost = cost;
    strncpy(node->product_name, name, sizeof(node->product_name));
    node->next = NULL;

    if(list->head == NULL){ // if this is the first node, gotta point head to it
        list->head = node;
        list->tail = node;  // for the first node, head and tail point to the same node
    }else{
        tail->next = node;  // append the node
        tail = node;        // point the tail at the end
    }
}

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

// return a pointer to element with product code
product_data_t*  seek(list_t *list, int code){
   product_data_t* iter = list->head;
   while(iter != NULL)
       if(iter->code == code)
           return iter;
       iter = iter->next;
   }
   return NULL; // element with code doesn't exist
}

Часто голова и хвост являются полностью построенными узлами, которые сами используются в качестве часового для обозначения начала и конца списка. Они сами не хранят данные (ну, скорее, их данные представляют собой маркер стража), они просто являются заполнителями для передней и задней частей. Это может упростить кодирование некоторых алгоритмов, связанных со связанными списками, за счет необходимости иметь два дополнительных элемента. В целом, связанные списки представляют собой гибкие структуры данных с несколькими способами их реализации.

о да, и Ник прав, игра со связанными списками - отличный способ справиться с указателями и косвенными ссылками. И они также отличный способ практиковать рекурсию! После того, как вы освоитесь со связным списком, попробуйте построить следующее дерево и использовать рекурсию для обхода дерева.

3 голосов
/ 11 июня 2009

Если вы хотите лучше понять основы связанных списков, взгляните на следующий документ:

http://cslibrary.stanford.edu/103/LinkedListBasics.pdf

2 голосов
/ 11 июня 2009

Если вы изучаете теорию указателя C, это хорошее упражнение. В противном случае это кажется слишком косвенным для кода, который не является универсальным (как в библиотеке).

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

Академически, структура kungfucraig s выглядит более общей, чем та, которую вы определили.

2 голосов
/ 11 июня 2009

Я не пишу код здесь, но вам нужно сделать следующее:

  • Создание и объект списка, он будет оставаться глобальным на протяжении всей программы.
  • Malloc размер продукта _ данные _ т.
  • Для первого элемента (head равен NULL), укажите head на неправильный адрес.
  • Чтобы добавить следующий элемент, перейдите в конец списка, а затем добавьте указатель неверного адреса к следующему из последнего элемента. (Следующим из последнего элемента всегда будет NULL, так что вы пройдете до конца.)
  • Забудь на некоторое время хвост.
1 голос
/ 31 мая 2018

Реализация связанного списка, основанная на реализации, используемой в ядре Linux:

// for 'offsetof', see: https://stackoverflow.com/q/6433339/5447906.
#include <stddef.h>

// See: https://stackoverflow.com/q/10269685/5447906.
#define CONTAINER_OF(ptr, type, member) \
    ( (type *) ((char *)(ptr) - offsetof(type, member)) )

// The macro can't be used for list head.
#define LIST_DATA(ptr, type, member) \
    CONTAINER_OF(ptr, type, member);

// The struct is used for both: list head and list nodes.
typedef struct list_node
{
    struct list_node *prev, *next;
}
list_node;

// List heads must be initialized by this function.
// Using the function for list nodes is not required.
static inline void list_head_init(list_node *node)
{
    node->prev = node->next = node;
}

// The helper function, mustn't be used directly.
static inline void list_add_helper(list_node *prev, list_node *next, list_node *nnew)
{
    next->prev = nnew;
    nnew->next = next;
    nnew->prev = prev;
    prev->next = nnew;
}

// 'node' must be a list head or a part of a list.
// 'nnew' must not be a list head or a part of a list. It may
//   be uninitialized or contain any data (even garbage).
static inline void list_add_after(list_node *node, list_node *nnew)
{
    list_add_helper(node, node->next, nnew);
}

// 'node' must be a list head or a part of a list.
// 'nnew' must not be a list head or a part of a list. It may
//   be uninitialized or contain any data (even garbage).
static inline void list_add_before(list_node *node, list_node *nnew)
{
    list_add_helper(node->prev, node, nnew);
}

// 'node' must be part of a list.
static inline list_node *list_del(list_node *node)
{
    node->prev->next = node->next;
    node->next->prev = node->prev;
    return node->prev;
}

Пример использования:

#include <stdio.h>

// The struct must contain 'list_node' to be able to be inserted to a list
typedef struct
{
    int       data;
    list_node node;
}
my_struct;

// Convert 'list_node *' to 'my_struct*' that contains this 'list_node'
static inline my_struct* get_my_struct(list_node *node_ptr)
{
    return LIST_DATA(node_ptr, my_struct, node);
}

void print_my_list(list_node *head)
{
    printf("list: {");
    for (list_node *cur = head->next; cur != head; cur = cur->next)
    {
        my_struct *my = get_my_struct(cur);
        printf(" %d", my->data);
    }
    printf(" }\n");
}

// Print 'cmd' and run it. Note: newline is not printed.
#define TRACE(cmd) \
    (printf("%s -> ", #cmd), (cmd))

int main()
{
    // The head of the list and the list itself. It doesn't contain any data.
    list_node head;
    list_head_init(&head);

    // The list's nodes, contain 'int' data in 'data' member of 'my_struct'
    my_struct el1 = {1};
    my_struct el2 = {2};
    my_struct el3 = {3};

    print_my_list(&head); // print initial state of the list (that is an empty list)

    // Run commands and print their result.
    TRACE( list_add_after (&head    , &el1.node) ); print_my_list(&head);
    TRACE( list_add_after (&head    , &el2.node) ); print_my_list(&head);
    TRACE( list_add_before(&el1.node, &el3.node) ); print_my_list(&head);
    TRACE( list_del       (head.prev)            ); print_my_list(&head);
    TRACE( list_add_before(&head    , &el1.node) ); print_my_list(&head);
    TRACE( list_del       (&el3.node)            ); print_my_list(&head);

    return 0;
}

Результат выполнения кода выше:

list: { }
list_add_after (&head , &el1.node) -> list: { 1 }
list_add_after (&head , &el2.node) -> list: { 2 1 }
list_add_before(&el1.node, &el3.node) -> list: { 2 3 1 }
list_del (head.prev) -> list: { 2 3 }
list_add_before(&head , &el1.node) -> list: { 2 3 1 }
list_del (&el3.node) -> list: { 2 1 }

http://coliru.stacked -crooked.com / а / 6e852a996fb42dc2

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

1 голос
/ 31 мая 2018

Демонстрация для Односвязный список . Если хотите, попробуйте проверить Круговой связанный список и Двойной связанный список .

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


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


// Iterating over a list
void
print_list(node_t *head)
{
    node_t *current = head;

    while(current != NULL)
    {
        printf("%d\n", current->val);
        current = current->next;
    }
}


// Adding an item to the end of the list
void
push_end(node_t *head, int val)
{
    node_t *current = head;
    while (current->next != NULL)
    {
        current = current->next;
    }

    current->next = malloc(sizeof(node_t));
    current->next->val = val;
    current->next->next = NULL;
}

// Adding an item to the head of the list
void
push_head(node_t **head, int val)
{
    node_t *new_node = NULL;

    new_node = malloc(sizeof(node_t));
    new_node->val = val;
    new_node->next = *head;

    *head = new_node;
}

// Removing the head item of the list
int
pop_head(node_t **head)
{
    int retval = -1;
    node_t *next_node = NULL;

    if (*head == NULL) {
        return -1;
    }

    next_node = (*head)->next;
    retval = (*head)->val;
    free(*head);
    *head = next_node;

    return retval;
}

// Removing the last item of the list
int
pop_last(node_t *head)
{
    int retval = 0;
    node_t *current = NULL;

    if (head->next == NULL) {
        retval = head->val;
        free(head);
        return retval;
    }

    /* get to the second to last node in the list */
    current = head;
    while (current->next->next != NULL) {
        current = current->next;
    }

    /* now current points to the second to last item of the list.
       so let's remove current->next */
    retval = current->next->val;
    free(current->next);
    current->next = NULL;

    return retval;
}

// Removing a specific item
int
remove_by_index(node_t **head, int n)
{
    int i = 0;
    int retval = -1;
    node_t *current = *head;
    node_t *temp_node = NULL;

    if (n == 0) {
        return pop_head(head);
    }

    for (i = 0; i < n - 1; i++) {
        if (current->next == NULL) {
            return -1;
        }
        current = current->next;
    }

    temp_node = current->next;
    retval = temp_node->val;
    current->next = temp_node->next;
    free(temp_node);

    return retval;
}


int
main(int argc, const char *argv[])
{
    int i;
    node_t * testnode;

    for (i = 0; i < argc; i++)
    {
        push_head(&testnode, atoi(argv[i]));
    }

    print_list(testnode);

    return 0;
}

// http://www.learn-c.org/en/Linked_lists
// https://www.geeksforgeeks.org/data-structures/linked-list/
1 голос
/ 04 марта 2018

Я думаю, что сначала нужно представить себе бэкэнд. Код ничего важного. Зайдите сюда и визуализируйте базовый базовый c код всей вставки. 1) Вставка в начале Посетите и прокрутите, чтобы получить каждое выполнение инструкции на сервере И тебе нужен фронт и воображение. Иди сюда Серверная часть

И все остальные возможные вставки здесь.

И важно, что вы можете использовать этот способ.

struct Node{
int data;//data field
struct Node*next;//pointer field
};

struct Node * голова, * хвост; // попробуем так

или короткий путь

struct Node{
int data;//data field
struct Node*next;//pointer field
}*head,*tail; //global root pointer

И

<< <a href="http://www.codelike.in/" rel="nofollow noreferrer"> Нажмите >> Для визуализации другой проблемы со связанным списком.

Благодарю.

1 голос
/ 11 июня 2009

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

Для простоты избавьтесь от отдельной структуры для головы и хвоста. Сделайте их глобальными переменными (в той же области, в которой они находятся сейчас) и измените их на listhead и listtail. Это сделает код намного более читабельным (вы не будете без необходимости проходить через отдельную структуру) и не ошибетесь, если выделите неправильную структуру.

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

1 голос
/ 11 июня 2009

Вы занимаете место для вашей структуры list_t, просто указатели на начало и конец списка, а это не то, что вы хотите.

Когда вы добавляете в связанный список, выделяйте место для фактического узла в списке, который является вашей структурой product_data_t.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...