------------------------- ------------------------
| | | \ | | |
| DATA | NEXT |-------------| DATA | NEXT |
| | | / | | |
------------------------- ------------------------
Одним из способов создания динамических структур данных в C является использование связанных списков. В этой статье я представляю основные методы добавления, удаления и поиска элемента.
Для этих примеров структура данных, которую мы используем, выглядит следующим образом:
typedef struct listas {
int info;
struct listas * next;
} list_t;
Добавить элемент в head:
list_t * listaddhead (list_t * t, int val)
{
list_t * new_item;
if((new_item = malloc(sizeof(list_t)))){
new_item->info = val;
new_item->next = t;
t = new_item;
} else
printf("out of memory");
return t;
}
Давайте посмотрим подробнее, метод добавления в начало получает указатель на первый элемент списка и значение для добавления в список.
Мы создадим экземпляр памяти с помощью MALLOC. Если памяти достаточно, мы присвоим значение, полученное на входе от метода, новому элементу, а в начало списка присвоим указатель на следующий элемент. Новый заголовок списка будет указателем на вновь созданный элемент.
Для большей наглядности сделаю несколько рисунков, имена переменных те же, что и в коде.
Это исходная ситуация:
------------- ------------------------ | | \ | | | \ | T(HEAD) |-------------| INFO | NEXT |------------- | | / | | | / ------------- ------------------------------------- ------------ | | \ | | |new_item|-------------| NULL | | | / | | ------------- ------------
Первое, что происходит, мы создаем экземпляр области памяти.
(new_item = malloc(sizeof(list_t))------------- ------------------------ | | \ | | | \ | T(HEAD) |-------------| INFO | NEXT |------- | | / | | | / ------------- ------------------------------------- ------------------------ | | \ | | | \ |new_item|-------------| INFO | NEXT |------- NULL | | / | | | / ------------- ------------------------
Теперь мы назначаем голову следующему новому элементу
new_item->next = t;------------- ------------------------ | | \ | | | \ | T(HEAD) |-------------| INFO | NEXT |------- | | | / | | | / ------------- | ------------------------ |---------------------------------- |------------- ------------------------ | | | \ | | | | |new_item|-------------| INFO | NEXT |--- | | / | | | ------------- ------------------------
Мы делаем так, чтобы заголовок указывал на новый элемент, и именно его возвращает метод.
t = new_item;------------- ------------------------ | | \ | | | \ | T(HEAD) |--- -------| INFO | NEXT |------- | | | | / | | | / ------------- | | ------------------------ | || ---------------------------------- | |------------- | ------------------------ | | | | \ | | | | |new_item|-------------| INFO | NEXT |--- | | / | | | ------------- ------------------------
Добавить элемент в хвост:
list_t * listaddtail (list_t * t, int val) /* add in queue */
{
list_t * new_item;
list_t * tmp1;
list_t * tmp2;
int i;
for(tmp2 = t, i = 0; tmp2; tmp1 = tmp2, tmp2 = tmp2->next)
i++;
if(i){
if((new_item = malloc(sizeof(list_t)))){
new_item->info = val;
new_item->next = NULL;
tmp1->next = new_item;
} else
printf("out of memory");
} else
t = listaddhead(t, val);
return t;
}
Как добавить элемент в конец списка? Сначала я прокручиваю список до последнего элемента, если список пуст, я вызываю метод добавления в начале, если он не пуст, я создаю экземпляр памяти для нового элемента. Я присваиваю значение, полученное в качестве входных данных от метода, для новый элемент, и я установил в NULL указатель на следующий объект.
В дополнение к последнему элементу в списке, мы собираемся сделать так, чтобы он указывал на вновь созданный элемент.
Это исходная ситуация:
-------------------- --------------------
\ | | | \ | | | \
-----| INFO | NEXT |------| INFO | NEXT |------ NULL
/ | | | / | | | /
-------------------- --------------------
Первое, что происходит, мы создаем экземпляр области памяти.
(new_item = malloc(sizeof(list_t)
TMP1 TMP2
-------------------- --------------------
\ | | | \ | | | \
-----| INFO | NEXT |------| INFO | NEXT |------ NULL
/ | | | / | | | /
-------------------- --------------------
------------ --------------------
| | \ | | | \
| new_item | ------| INFO | NEXT |------ NULL
| | / | | | /
------------ --------------------
Теперь давайте укажем последний элемент списка на новый элемент
tmp1->next = new_item;
TMP1
-------------------- --------------------
\ | | | \ | | |
-----| INFO | NEXT |------| INFO | NEXT |----
/ | | | / | | | |
-------------------- -------------------- |
-----------------------------------------
------------ | --------------------
| | | \ | | | \
| new_item | ------| INFO | NEXT |------ NULL
| | / | | | /
------------ --------------------
Удалить элемент
Чтобы удалить элемент, я прокручиваю список, пока не найду этот элемент, и при прокрутке списка я сохраняю указатель на текущий элемент и указатель на предыдущий элемент.
Как только я нахожу элемент, я обновляю указатель предыдущего элемента с указателем элемента, следующего за удаляемым. Затем мы освобождаем память элемента, который хотим удалить.
Сначала мы находим элемент для удаления
for(tmp1 = NULL, tmp2 = t; tmp2->next && tmp2->info != val; tmp1 = tmp2, tmp2 = tmp2->next);
TMP1 TMP2 (TO DELETE)
--------------- --------------- ---------------
\ | | | \ | | | \ | | | \
----| INFO | NEXT |----| INFO | NEXT |----| INFO | NEXT |----
/ | | | / | | | / | | | /
--------------- --------------- ---------------
Мы обновляем указатель
tmp1->next = tmp2->next;
free (tmp2);
TMP1 TMP2 (TO DELETE)
--------------- --------------- ---------------
\ | | | | | | \ | | | \
----| INFO | NEXT |-- | INFO | NEXT | ---| INFO | NEXT |----
/ | | | | | | | |/ | | | /
--------------- | --------------- | ---------------
--------------------
Освободить память
free (tmp2);
TMP1
--------------- ---------------
\ | | | \ | | | \
----| INFO | NEXT |-- ---| INFO | NEXT |----
/ | | | | |/ | | | /
--------------- | | ---------------
--------------------
Это основные методы управления списком в C.
Я помещаю здесь полный код, а также основной, чтобы попытаться выполнить код.
Compile: gcc -include list.c main.c Run: ./a.out
Если у вас возникли проблемы или вы решили их каким-либо другим способом, не стесняйтесь, пишите в комментариях!
Чтобы получить доступ к неограниченному количеству историй, вы также можете зарегистрироваться и стать участником Medium всего за 5 долларов США. Если вы зарегистрируетесь по моей ссылке, я получу небольшую комиссию.