Перейти к содержимому

Как добавить элемент в список c

  • автор:

Как добавить элемент в список в с++? Как создать список в с++?

Задача такова нужно вывести все числа, до какого-то а. Циклы использовать нельзя. На питоне будет примерно так:

a = int(input("a mt24 mb12">
    c++рекурсия
)" data-controller="se-share-sheet" data-se-share-sheet-title="Поделиться ссылкой на вопрос" data-se-share-sheet-subtitle="" data-se-share-sheet-post-type="question" data-se-share-sheet-social="facebook twitter " data-se-share-sheet-location="1" data-se-share-sheet-license-url="https%3a%2f%2fcreativecommons.org%2flicenses%2fby-sa%2f4.0%2f" data-se-share-sheet-license-name="CC BY-SA 4.0" data-s-popover-placement="bottom-start">Поделиться
)" title="">Улучшить вопрос
)">изменён 2 фев 2019 в 7:36
Harry
219k15 золотых знаков119 серебряных знаков230 бронзовых знаков
задан 2 фев 2019 в 6:29
1
    Если вы создадите список - то как будете выводить его без цикла? Не то чтобы это было невозможно :), но просто интересен ход ваших мыслей.
    – Harry
    2 фев 2019 в 7:10
Добавить комментарий|

2 ответа 2

Сброс на вариант по умолчанию
2

Именно вывести? тогда зачем вам список?

void out(int n, int curr = 0)

На всякий случай - а то вы стали создавать вложенные функции 🙁 - вот весь код:

#include using namespace std; void out(int n, int curr = 0) < if (n < curr) return; cout int main(int argc, const char * argv[]) < int m, M; cout > m; cout > M; out(M,m); > 

Как добавить элемент в список c

Контейнер list представляет двухсвязный список, то есть такой список, где каждый элемент имеет указатели на предыдущий и последовательный элемент. Благодаря чему мы можем перемещаться по списку как вперед, так и назад. Для использования списка необходимо подключить заголовочный файл list .

std::list list1; // пустой список std::list list2(5); // список list2 состоит из 5 чисел, каждый элемент имеет значение по умолчанию std::list list3(5, 2); // список list3 состоит из 5 чисел, каждое число равно 2 std::list list4< 1, 2, 4, 5 >; // список list4 состоит из чисел 1, 2, 4, 5 std::list list5 = < 1, 2, 3, 5 >; // список list5 состоит из чисел 1, 2, 4, 5 std::list list6(list4); // список list6 - копия списка list4 std::list list7 = list4; // список list7 - копия списка list4

Получение элементов

В отличие от других контейнеров для типа list не определена операция обращения по индексу или функция at(), которая выполняет похожую задачу.

Тем не менее для контейнера list можно использовать функции front() и back() , которые возвращают соответственно первый и последний элементы.

Чтобы обратиться к элементам, которые находятся в середине (после первого и до последнего элементов), придется выполнять перебор элементов с помощью циклов или итераторов:

#include #include int main() < std::listnumbers< 1, 2, 3, 4, 5 >; int first ; // 1 int last < numbers.back() >; // 5 std::cout std::cout

Размер списка

Для получения размера списка можно использовать функцию size() :

std::list numbers< 1, 2, 3, 4, 5 >; int size = numbers.size(); // 5

Функция empty() позволяет узнать, пуст ли список. Если он пуст, то функция возвращает значение true, иначе возвращается значение false:

std::list numbers< 1, 2, 3, 4, 5 >; if (numbers.empty()) std::cout 

С помощью функции resize() можно изменить размер списка. Эта функция имеет две формы:

  • resize(n) : оставляет в списке n первых элементов. Если список содержит больше элементов, то он усекается до первых n элементов. Если размер списка меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию
  • resize(n, value) : также оставляет в списке n первых элементов. Если размер списка меньше n, то добавляются недостающие элементы со значением value
std::list numbers< 1, 2, 3, 4, 5, 6 >; numbers.resize(4); // оставляем первые четыре элемента - numbers = numbers.resize(6, 8); // numbers =

Изменение элементов списка

Функция assign() позволяет заменить все элементы списка определенным набором. Она имеет следующие формы:

  • assign(il) : заменяет содержимое контейнера элементами из списка инициализации il
  • assign(n, value) : заменяет содержимое контейнера n элементами, которые имеют значение value
  • assign(begin, end) : заменяет содержимое контейнера элементами из диапазона, на начало и конец которого указывают итераторы begin и end
std::list numbers < 1, 2, 3, 4, 5 >; numbers.assign(< 21, 22, 23, 24, 25 >); // numbers = < 21, 22, 23, 24, 25 >numbers.assign(4, 3); // numbers = std::list values < 6, 7, 8, 9, 10, 11 >; auto start = ++values.begin(); // итератор указывает на второй элемент из values auto end = values.end(); numbers.assign(start, end); // numbers =

Функция swap() обменивает значениями два списка:

std::list list1< 1, 2, 3, 4, 5 >; std::list list2< 6, 7, 8, 9>; list1.swap(list2); // list1 = < 6, 7, 8, 9>; // list2 = < 1, 2, 3, 4, 5 >;

Добавление элементов

Для добавления элементов в контейнер list применяется ряд функций.

  • push_back(val) : добавляет значение val в конец списка
  • push_front(val) : добавляет значение val в начало списка
  • emplace_back(val) : добавляет значение val в конец списка
  • emplace_front(val) : добавляет значение val в начало списка
  • emplace(pos, val) : вставляет элемент val на позицию, на которую указывает итератор pos. Возвращает итератор на добавленный элемент
  • insert(pos, val) : вставляет элемент val на позицию, на которую указывает итератор pos, аналогично функции emplace. Возвращает итератор на добавленный элемент
  • insert(pos, n, val) : вставляет n элементов val начиная с позиции, на которую указывает итератор pos. Возвращает итератор на первый добавленный элемент. Если n = 0, то возвращается итератор pos.
  • insert(pos, begin, end) : вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end. Возвращает итератор на первый добавленный элемент. Если между итераторами begin и end нет элементов, то возвращается итератор pos.
  • insert(pos, values) : вставляет список значений values начиная с позиции, на которую указывает итератор pos. Возвращает итератор на первый добавленный элемент. Если values не содержит элементов, то возвращается итератор pos.

Функции push_back() , push_front() , emplace_back() и emplace_front() :

std::list numbers< 1, 2, 3, 4, 5 >; numbers.push_back(23); // < 1, 2, 3, 4, 5, 23 >numbers.push_front(15); // < 15, 1, 2, 3, 4, 5, 23 >numbers.emplace_back(24); // < 15, 1, 2, 3, 4, 5, 23, 24 >numbers.emplace_front(14); //

Добавление в середину списка с помощью функции emplace() :

std::list numbers< 1, 2, 3, 4, 5 >; auto iter = ++numbers.cbegin(); // итератор указывает на второй элемент numbers.emplace(iter, 8); // добавляем после первого элемента numbers = < 1, 8, 2, 3, 4, 5>;

Добавление в середину списка с помощью функции insert() :

std::list numbers1< 1, 2, 3, 4, 5 >; auto iter1 = numbers1.cbegin(); // итератор указывает на первый элемент numbers1.insert(iter1, 0); // добавляем начало списка //numbers1 = < 0, 1, 2, 3, 4, 5>; std::list numbers2< 1, 2, 3, 4, 5 >; auto iter2 = numbers2.cbegin(); // итератор указывает на первый элемент numbers2.insert(++iter2, 3, 4); // добавляем после первого элемента три четверки //numbers2 = < 1, 4, 4, 4, 2, 3, 4, 5>; std::list values < 10, 20, 30, 40, 50 >; std::list numbers3< 1, 2, 3, 4, 5 >; auto iter3 = numbers3.cbegin(); // итератор указывает на первый элемент // добавляем в начало все элементы из values numbers3.insert(iter3, values.begin(), values.end()); //numbers3 = < 10, 20, 30, 40, 50, 1, 2, 3, 4, 5>; std::list numbers4< 1, 2, 3, 4, 5 >; auto iter4 = numbers4.cend(); // итератор указывает на позицию за последним элементом // добавляем в конец список из трех элементов numbers4.insert(iter4, < 21, 22, 23 >); //numbers4 = < 1, 2, 3, 4, 5, 21, 22, 23>;

Удаление элементов

Для удаления элементов из контейнера list могут применяться следующие функции:

  • clear(p) : удаляет все элементы
  • pop_back() : удаляет последний элемент
  • pop_front() : удаляет первый элемент
  • erase(p) : удаляет элемент, на который указывает итератор p. Возвращает итератор на элемент, следующий после удаленного, или на конец контейнера, если удален последний элемент
  • erase(begin, end) : удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, или на конец контейнера, если удален последний элемент

std::list numbers < 1, 2, 3, 4, 5 >; numbers.pop_front(); // numbers = < 2, 3, 4, 5 >numbers.pop_back(); // numbers = < 2, 3, 4 >numbers.clear(); // numbers =<> numbers = < 1, 2, 3, 4, 5 >; auto iter = numbers.cbegin(); // указатель на первый элемент numbers.erase(iter); // удаляем первый элемент // numbers = < 2, 3, 4, 5 >numbers = < 1, 2, 3, 4, 5 >; auto begin = numbers.begin(); // указатель на первый элемент auto end = numbers.end(); // указатель на последний элемент numbers.erase(++begin, --end); // удаляем со второго элемента до последнего //numbers =

Односвязный список

О дносвязный список – структура данных, в которой каждый элемент (узел) хранит информацию, а также ссылку на следующий элемент. Последний элемент списка ссылается на NULL.

Для нас односвязный список полезен тем, что

  • 1) Он очень просто устроен и все алгоритмы интуитивно понятны
  • 2) Односвязный список – хорошее упражнение для работы с указателями
  • 3) Его очень просто визаулизировать, это позволяет "в картинках" объяснить алгоритм
  • 4) Несмотря на свою простоту, односвязные списки часто используются в программировании, так что это не пустое упражнение.
  • 5) Эта структуру данных можно определить рекурсивно, и она часто используется в рекурсивных алгоритмах.

Для простоты рассмотрим односвязный список, который хранит целочисленное значение.

Односвязный список

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

typedef struct Node < int value; struct Node *next; >Node;

Чтобы не писать каждый раз struct мы определили новый тип.
Теперь наша задача написать функцию, которая бы собирала список из значений, которые мы ей передаём. Стандартное имя функции – push, она должна получать в качестве аргумента значение, которое вставит в список. Новое значение будет вставляться в начало списка. Каждый новый элемент списка мы должны создавать на куче. Следовательно, нам будет удобно иметь один указатель на первый элемент списка.

Node *head = NULL;

Вначале списка нет и указатель ссылается на NULL.
Для добавления нового узла необходимо

  • 1) Выделить под него память.
  • 2) Задать ему значение
  • 3) Сделать так, чтобы он ссылался на предыдущий элемент (или на NULL, если его не было)
  • 4) Перекинуть указатель head на новый узел.

1) Создаём новый узел

Создали новый узел, на который ссылается локальная переменная tmp

2) Присваиваем ему значение

Присвоили ему значение

3) Присваиваем указателю tmp адрес предыдущего узла

Перекинули указатель tmp на предыдущий узел

4) Присваиваем указателю head адрес нового узла

Перекинули указатель head на вновь созданный узел tmp

5) После выхода из функции переменная tmp будет уничтожена. Получим список, в который будет вставлен новый элемент.

Новый узел добавлен

void push(Node **head, int data) < Node *tmp = (Node*) malloc(sizeof(Node)); tmp->value = data; tmp->next = (*head); (*head) = tmp; >

Так как указатель head изменяется, то необходимо передавать указатель на указатель.

Теперь напишем функцию pop: она удаляет элемент, на который указывает head и возвращает его значение.

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

Локальная переменная хранит адрес первого узла

Уже после этого можно удалить первый элемент и вернуть его значение

Перекинули указатель head на следующий элемент и удалили узел

int pop(Node **head) < Node* prev = NULL; int val; if (head == NULL) < exit(-1); >prev = (*head); val = prev->value; (*head) = (*head)->next; free(prev); return val; >

Не забываем, что необходимо проверить на NULL голову.

Таким образом, мы реализовали две операции push и pop, которые позволяют теперь использовать односвязный список как стек. Теперь добавим ещё две операции - pushBack (её ещё принято называть shift или enqueue), которая добавляет новый элемент в конец списка, и функцию popBack (unshift, или dequeue), которая удаляет последний элемент списка и возвращает его значение.

Для дальнейшего разговора необходимо реализовать функции getLast, которая возвращает указатель на последний элемент списка, и nth, которая возвращает указатель на n-й элемент списка.

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

Node* getNth(Node* head, int n) < int counter = 0; while (counter < n && head) < head = head->next; counter++; > return head; >

Переходя на следующий элемент не забываем проверять, существует ли он. Вполне возможно, что был указан номер, который больше размера списка. Функция вернёт в таком случае NULL. Сложность операции O(n), и это одна из проблем односвязного списка.

Для нахождение последнего элемента будем передирать друг за другом элементы до тех пор, пока указатель next одного из элементов не станет равным NULL

Node* getLast(Node *head) < if (head == NULL) < return NULL; >while (head->next) < head = head->next; > return head; >

Теперь добавим ещё две операции - pushBack (её ещё принято называть shift или enqueue), которая добавляет новый элемент в конец списка, и функцию popBack (unshift, или dequeue), которая удаляет последний элемент списка и возвращает его значение.

Для вставки нового элемента в конец сначала получаем указатель на последний элемент, затем создаём новый элемент, присваиваем ему значение и перекидываем указатель next старого элемента на новый

void pushBack(Node *head, int value) < Node *last = getLast(head); Node *tmp = (Node*) malloc(sizeof(Node)); tmp->value = value; tmp->next = NULL; last->next = tmp; >

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

Node* getLastButOne(Node* head) < if (head == NULL) < exit(-2); >if (head->next == NULL) < return NULL; >while (head->next->next) < head = head->next; > return head; >

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

void popBack(Node **head) < Node *lastbn = NULL; //Получили NULL if (!head) < exit(-1); >//Список пуст if (!(*head)) < exit(-1); >lastbn = getLastButOne(*head); //Если в списке один элемент if (lastbn == NULL) < free(*head); *head = NULL; >else < free(lastbn->next); lastbn->next = NULL; > >

Удаление последнего элемента и вставка в конец имеют сложность O(n).

Можно написать алгоритм проще. Будем использовать два указателя. Один – текущий узел, второй – предыдущий. Тогда можно обойтись без вызова функции getLastButOne:

int popBack(Node **head) < Node *pFwd = NULL; //текущий узел Node *pBwd = NULL; //предыдущий узел //Получили NULL if (!head) < exit(-1); >//Список пуст if (!(*head)) < exit(-1); >pFwd = *head; while (pFwd->next) < pBwd = pFwd; pFwd = pFwd->next; > if (pBwd == NULL) < free(*head); *head = NULL; >else < free(pFwd->next); pBwd->next = NULL; > >

Теперь напишем функцию insert, которая вставляет на n-е место новое значение. Для вставки, сначала нужно будет пройти до нужного узла, потом создать новый элемент и поменять указатели. Если мы вставляем в конец, то указатель next нового узла будет указывать на NULL, иначе на следующий элемент

void insert(Node *head, unsigned n, int val) < unsigned i = 0; Node *tmp = NULL; //Находим нужный элемент. Если вышли за пределы списка, то выходим из цикла, //ошибка выбрасываться не будет, произойдёт вставка в конец while (i < n && head->next) < head = head->next; i++; > tmp = (Node*) malloc(sizeof(Node)); tmp->value = val; //Если это не последний элемент, то next перекидываем на следующий узел if (head->next) < tmp->next = head->next; //иначе на NULL > else < tmp->next = NULL; > head->next = tmp; >

Покажем на рисунке последовательность действий

Создали новый узел и присвоили ему значение

После этого делаем так, чтобы новый элемент ссылался на следующий после n-го

Теперь значение next нового узла хранит адрес того же узла, что и элемент, на который ссылается head

Перекидываем указатель next n-го элемента на вновь созданный узел

Теперь узел, адрес которого хранит head, указывает на новый узел tmp

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

int deleteNth(Node **head, int n) < if (n == 0) < return pop(head); >else < Node *prev = getNth(*head, n-1); Node *elm = prev->next; int val = elm->value; prev->next = elm->next; free(elm); return val; > >

Рассмотрим то же самое в картинках. Сначала находим адреса удаляемого элемента и того, который стоит перед ним

Для удаления узла, на который ссылается elm необходим предыдущий узел, адрес которого хранит prev

После чего прокидываем указатель next дальше, а сам элемент удаляем.

Прекидываем указатель на следующий за удалённым узел и освобождаем память

Кроме создания списка необходимо его удаление. Так как самая быстрая функция у нас этот pop, то для удаления будем последовательно выталкивать элементы из списка.

void deleteList(Node **head) < while ((*head)->next) < pop(head); *head = (*head)->next; > free(*head); >

Вызов pop можно заменить на тело функции и убрать ненужные проверки и возврат значения

void deleteList(Node **head) < Node* prev = NULL; while ((*head)->next) < prev = (*head); (*head) = (*head)->next; free(prev); > free(*head); >

Осталось написать несколько вспомогательных функций, которые упростят и ускорят работу. Первая - создать список из массива. Так как операция push имеет минимальную сложность, то вставлять будем именно с её помощью. Так как вставка произойдёт задом наперёд, то массив будем обходить с конца к началу:

void fromArray(Node **head, int *arr, size_t size) < size_t i = size - 1; if (arr == NULL || size == 0) < return; >do < push(head, arr[i]); >while(i--!=0); >

И обратная функция, которая возвратит массив элементов, хранящихся в списке. Так как мы будем создавать массив динамически, то сначала определим его размер, а только потом запихнём туда значения.

int* toArray(const Node *head) < int leng = length(head); int *values = (int*) malloc(leng*sizeof(int)); while (head) < values[--leng] = head->value; head = head->next; > return values; >

И ещё одна функция, которая будет печатать содержимое списка

void printLinkedList(const Node *head) < while (head) < printf("%d ", head->value); head = head->next; > printf("\n"); >

Теперь можно провести проверку и посмотреть, как работает односвязный список

void main() < Node* head = NULL; int arr[] = ; //Создаём список из массива fromArray(&head, arr, 10); printLinkedList(head); //Вставляем узел со значением 333 после 4-го элемента (станет пятым) insert(head, 4, 333); printLinkedList(head); pushBack(head, 11); pushBack(head, 12); pushBack(head, 13); pushBack(head, 14); printLinkedList(head); printf("%d\n", pop(&head)); printf("%d\n", popBack(&head)); printLinkedList(head); //Удаляем пятый элемент (индексация с нуля) deleteNth(&head, 4); printLinkedList(head); deleteList(&head); getch(); >

Сортировка односвязного списка

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

Вспомогательная функция – слияние двух отсортированных списков. Функция не должна создавать новых узлов, так что будем использовать только имеющиеся. Для начала проверим, если хоть один из списков пуст, то вернём другой.

Node tmp; *c = NULL; if (a == NULL) < *c = b; return; >if (b == NULL)

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

if (a->value < b->value) < *c = a; a = a->next; > else < *c = b; b = b->next; >

Теперь сохраним указатель c, так как в дальнейшем он будет использоваться для прохода по списку

tmp.next = *c;

После этого проходим по спискам, сравниваем значения и перекидываем их

while (a && b) < if (a->value < b->value) < (*c)->next = a; a = a->next; > else < (*c)->next = b; b = b->next; > (*c) = (*c)->next; >

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

if (a) < while (a) < (*c)->next = a; (*c) = (*c)->next; a = a->next; > > if (b) < while (b) < (*c)->next = b; (*c) = (*c)->next; b = b->next; > >

Теперь указатель c хранит адрес последнего узла, а нам нужна ссылка на голову. Она как раз хранится во второй переменной tmp

*c = tmp.next;
void merge(Node *a, Node *b, Node **c) < Node tmp; *c = NULL; if (a == NULL) < *c = b; return; >if (b == NULL) < *c = a; return; >if (a->value < b->value) < *c = a; a = a->next; > else < *c = b; b = b->next; > tmp.next = *c; while (a && b) < if (a->value < b->value) < (*c)->next = a; a = a->next; > else < (*c)->next = b; b = b->next; > (*c) = (*c)->next; > if (a) < while (a) < (*c)->next = a; (*c) = (*c)->next; a = a->next; > > if (b) < while (b) < (*c)->next = b; (*c) = (*c)->next; b = b->next; > > *c = tmp.next; >

Ещё одна важная функция – нахождение середины списка. Для этих целей будем использовать два указателя. Один из них - fast – за одну итерацию будет два раза изменять значение и продвигаться по списку вперёд. Второй – slow, всего один раз. Таким образом, если список чётный, то slow окажется ровно посредине списка, а если список нечётный, то второй подсписок будет на один элемент длиннее.

void split(Node *src, Node **low, Node **high) < Node *fast = NULL; Node *slow = NULL; if (src == NULL || src->next == NULL) < (*low) = src; (*high) = NULL; return; >slow = src; fast = src->next; while (fast != NULL) < fast = fast->next; if (fast != NULL) < fast = fast->next; slow = slow->next; > > (*low) = src; (*high) = slow->next; slow->next = NULL; >

Очевидно, что можно было один раз узнать длину списка, а потом передавать размер в каждую функцию. Это было бы проще и быстрее. Но мы не ищем лёгких путей)))

Теперь у нас есть функция, которая позволяет разделить список на две части и функция слияния отсортированных списков. С их помощью реализуем функцию сортировки слиянием.

Сортировка слиянием для односвязного списка

Функция рекурсивно вызывает сама себя, передавая части списка. Если в функцию пришёл список длинной менее двух элементов, то рекурсия прекращается. Идёт обратная сборка списка. Сначала из двух списков, каждый из которых хранит один элемент, создаётся отсортированный список, далее из таких списков собирается новый отсортированный список, пока все элементы не будут включены.

void mergeSort(Node **head) < Node *low = NULL; Node *high = NULL; if ((*head == NULL) || ((*head)->next == NULL)) < return; >split(*head, &low, &high); mergeSort(&low); mergeSort(&high); merge(low, high, head); >

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

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Односвязный линейный список

Односвязный линейный список

Каждый узел однонаправленного (односвязного) линейного списка (ОЛС) содержит одно поле указателя на следующий узел. Поле указателя последнего узла содержит нулевое значение (указывает на NULL).

Узел ОЛС можно представить в виде структуры

struct list
int field; // поле данных
struct list *ptr; // указатель на следующий элемент
>;

Основные действия, производимые над элементами ОЛС:

  • Инициализация списка
  • Добавление узла в список
  • Удаление узла из списка
  • Удаление корня списка
  • Вывод элементов списка
  • Взаимообмен двух узлов списка

Инициализация ОЛС

Инициализация односвязного линейного списка

Инициализация списка предназначена для создания корневого узла списка, у которого поле указателя на следующий элемент содержит нулевое значение.

struct list * init( int a) // а- значение первого узла
struct list *lst;
// выделение памяти под корень списка
lst = ( struct list*)malloc( sizeof ( struct list));
lst->field = a;
lst->ptr = NULL ; // это последний узел списка
return (lst);
>

Добавление узла в ОЛС

Функция добавления узла в список принимает два аргумента:

  • Указатель на узел, после которого происходит добавление
  • Данные для добавляемого узла.

Добавление элемента односвязного линейного списка

Процедуру добавления узла можно отобразить следующей схемой:

Добавление узла в ОЛС включает в себя следующие этапы:

  • создание добавляемого узла и заполнение его поля данных;
  • переустановка указателя узла, предшествующего добавляемому, на добавляемый узел;
  • установка указателя добавляемого узла на следующий узел (тот, на который указывал предшествующий узел).

Таким образом, функция добавления узла в ОЛС имеет вид:

1
2
3
4
5
6
7
8
9
10

struct list * addelem(list *lst, int number)
struct list *temp, *p;
temp = ( struct list*)malloc( sizeof (list));
p = lst->ptr; // сохранение указателя на следующий узел
lst->ptr = temp; // предыдущий узел указывает на создаваемый
temp->field = number; // сохранение поля данных добавляемого узла
temp->ptr = p; // созданный узел указывает на следующий элемент
return (temp);
>

Возвращаемым значением функции является адрес добавленного узла.

Удаление узла ОЛС

В качестве аргументов функции удаления элемента ОЛС передаются указатель на удаляемый узел, а также указатель на корень списка.
Функция возвращает указатель на узел, следующий за удаляемым.

Удаление элемента односвязного линейного списка

Удаление узла может быть представлено следующей схемой:

Удаление узла ОЛС включает в себя следующие этапы:

  • установка указателя предыдущего узла на узел, следующий за удаляемым;
  • освобождение памяти удаляемого узла.

1
2
3
4
5
6
7
8
9
10
11
12

struct list * deletelem(list *lst, list *root)
struct list *temp;
temp = root;
while (temp->ptr != lst) // просматриваем список начиная с корня
< // пока не найдем узел, предшествующий lst
temp = temp->ptr;
>
temp->ptr = lst->ptr; // переставляем указатель
free(lst); // освобождаем память удаляемого узла
return (temp);
>

Удаление корня списка

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

struct list * deletehead(list *root)
struct list *temp;
temp = root->ptr;
free(root); // освобождение памяти текущего корня
return (temp); // новый корень списка
>

Вывод элементов списка

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

void listprint(list *lst)
struct list *p;
p = lst;
do printf( "%d " , p->field); // вывод значения элемента p
p = p->ptr; // переход к следующему узлу
> while (p != NULL );
>

Взаимообмен узлов ОЛС

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

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

  • заменяемые узлы являются соседями;
  • заменяемые узлы не являются соседями, то есть между ними имеется хотя бы один элемент.

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

Функция взаимообмена узлов списка выглядит следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

struct list * swap( struct list *lst1, struct list *lst2, struct list *head)
// Возвращает новый корень списка
struct list *prev1, *prev2, *next1, *next2;
prev1 = head;
prev2 = head;
if (prev1 == lst1)
prev1 = NULL ;
else
while (prev1->ptr != lst1) // поиск узла предшествующего lst1
prev1 = prev1->ptr;
if (prev2 == lst2)
prev2 = NULL ;
else
while (prev2->ptr != lst2) // поиск узла предшествующего lst2
prev2 = prev2->ptr;
next1 = lst1->ptr; // узел следующий за lst1
next2 = lst2->ptr; // узел следующий за lst2
if (lst2 == next1)
< // обмениваются соседние узлы
lst2->ptr = lst1;
lst1->ptr = next2;
if (lst1 != head)
prev1->ptr = lst2;
>
else
if (lst1 == next2)
// обмениваются соседние узлы
lst1->ptr = lst2;
lst2->ptr = next1;
if (lst2 != head)
prev2->ptr = lst2;
>
else
// обмениваются отстоящие узлы
if (lst1 != head)
prev1->ptr = lst2;
lst2->ptr = next1;
if (lst2 != head)
prev2->ptr = lst1;
lst1->ptr = next2;
>
if (lst1 == head)
return (lst2);
if (lst2 == head)
return (lst1);
return (head);
>

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *