C++. Линейный односвязный список. Общие сведения

Линейный односвязный список. Общие сведения. Базовые операции над списком


Содержание


Поиск на других ресурсах:

1. Понятие об односвязном списке. Особенности кодировки. Базовые операции над списком

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

Односвязный список может сохраняться:

  • в оперативной памяти;
  • в файле.

В односвязном списке выделяют отдельный элемент (Element), который называется узел (Node).

Каждый элемент (узел) односвязного списка состоит из двух частей:

  • данные. Это может быть переменная любого примитивного типа (int, double, char, …), объект класса или сложная структура. Данные могут состоять из нескольких полей (переменных);
  • адрес или позиция следующего элемента. Если односвязный список хранится в оперативной памяти, то это указатель (адрес) на такой же элемент (узел). Если односвязный список хранится в файле, то это позиция следующего элемента в файле.

Реализовать узел можно с помощью класса или структуры. Также можно создавать односвязный список на основе шаблонного класса, оперирующего некоторым обобщенным типом T.

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

Для списка можно выделить следующие основные операции:

  • формирование списка;
  • добавление элемента в конец списка;
  • вставка элемента в заданную позицию списка;
  • удаление элемента из списка из заданной позиции;
  • очистка списка;
  • замена элемента в списке;
  • поиск элемента в списке по индексу или некоторому критерию.

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

 

2. Общий вид списка. Структура, описывающая один элемент списка

Общий вид линейного односвязного списка изображен на рисунке 1.

C++. Линейный односвязный (однонаправленный) список. Общий видРисунок 1. Общий вид списка

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

C++. Линейный односвязный список. Структура Element - элемент списка

Рисунок 2. Структура Element, описывающая один элемент списка с программным кодом на языке C++

 

3. Формирование списка
3.1. Создать пустой список

Создание списка включает следующие этапы.
1. Объявление указателей на начало и конец списка.
2. Установка нулевых значений указателям.
3. Установка количества элементов в значение 0.

 

C++. Линейный односвязный список. Создание списка

Рисунок 3. Формирование списка: 1) — объявление указателей; 2) — установка нулевых значений

 

3.2. Добавление элемента в конец списка

При добавлении элемента в конец списка рассматривают 2 возможные ситуации:

  • добавление элемента в пустой список;
  • добавление элемента в непустой список (список, в котором есть элементы).

 

3.2.1. Добавление нового элемента в конец пустого списка

При добавлении элемента в конец пустого списка выделяют следующие операции (рисунок 4):

1. Создать новый элемент. Здесь нужно выделить память для нового элемента и заполнить ее некоторыми данными (_data). Для этого объявляется указатель (например, elem):

Element<T>* elem = new Element<T>;
elem->data = _data;

2. Установить указатель next в нулевое значение

elem->next = nullptr;

3. Установить указатели begin и end равными значению указателя elem.

begin = end = elem;

4. Увеличить количество элементов в списке на 1

count++;

C++. Линейный односвязный список. Добавление элемента в конец пустого списка

Рисунок 4. Добавление элемента в конец пустого списка: 1 – создание элемента; 2 – установка указателя next; 3 – установка указателей begin и end

 

3.2.2. Добавление элемента в конец существующего списка

Если список уже существует, то последовательность шагов добавляющих элемент в список следующая (рисунок 5):

1. Создать новый элемент и заполнить его данными (рисунок 5). Заполняются поля data и next. Создание элемента осуществляется таким же образом как в случае с пустым списком:

Element<T>* elem = new Element<T>;
elem->data = _data;
elem->next = nullptr;

2. Установить указатель next элемента, на который указывает указатель end, в значение адреса элемента elem (рисунок 6)

end->next = elem;

3. Установить указатель end равным значению указателя elem

end = elem;

4. Увеличить количество элементов в списке на 1

count++;

Вышеприведенные шаги более красноречиво отображаются на рисунках 5, 6, 7.

C++. Добавление элемента в конец списка. Шаг 1. Создание элемента

Рисунок 5. Создание элемента и заполнение его данными

C++. Добавление элемента в конец списка. Шаг 2. Изменение указателя endРисунок 6. Изменение указателя next элемента end

C++. Добавление элемента в конец списка. Установка указателя end на последний элемент спискаРисунок 7. Установка указателя end на последний элемент списка

 

4. Вставка элемента в заданную позицию списка

При вставке элемента в заданную позицию списка рассматриваются 2 варианта:

  • список пуст (нет элементов);
  • список имеет хотя бы один элемент.

Если список пуст, то вставка заменяется добавлением первого элемента, как описано в пункте 3.2.1.

Если в списке есть элементы, то здесь рассматриваются 3 варианта:

  • вставка элемента в первую позицию списка;
  • вставка элемента внутри списка;
  • вставка элемента за последним элементом списка. Если происходит вставка за последним элементом списка, то это есть добавление элемента в конец списка как описано в пункте 3.2.2.

 

4.1. Список не пуст. Вставка элемента в первую позицию списка

Если происходит вставка элемента в первую позицию списка, то последовательность операций следующая:

1. Создайть новый элемент (рисунок 8). Выделить память под новый элемент. Заполнить элемент данными.

// Выделение памяти под новую ячейку
Element<T>* elem = new Element<T>;
elem->data = _data;

2. Установить указатель next элемента на начало списка (рисунок 9).

elem->next = begin;

3. Установить начало списка на новую ячейку (рисунок 10).

begin = elem;

Все вышеперечисленные шаги графически изображены на рисунках 8, 9, 10.

C++. Линейный односвязный список. Вставка элемента. Шаг 1. Создание элементаРисунок 8. Создание нового элемента. Заполнение полей элемента значениями

C++. Вставка элемента в начало списка. Шаг 2. Установка поля nextРисунок 9. Вставка элемента в начало списка. Установка поля next равного адресу первого элемента списка

C++. Вставка элемента в начало списка. Шаг 3. Изменение указателя начала спискаРисунок 10. Присваивание указателю начала списка адреса новосозданного элемента

 

4.2. Список не пуст. Вставка элемента в середину списка

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

1. Создать элемент. Заполнить поле данных элемента

Element<T>* elem = new Element<T>;
elem->data = _data;

2. Определить позицию элемента, который следует перед позицией вставки элемента. Получить указатель на эту позицию.

Element<T>* elemPrev = Move(index - 1);

здесь Move() – метод, возвращающий позицию элемента, находящегося перед позицией вставки index.

3. Установить указатель next элемента elem, который вставляется

elem->next = elemPrev->next;

4. Установить указатель next предыдущего элемента elemPrev так, чтобы он указывал на вставляемый элемент elem

elemPrev->next = elem;

Предыдущие 4 шага изображены на рисунках 11, 12, 13, 14.

C++. Вставка элемента всередину непустого списка. Создание элемента. Шаг 1. Заполнение элемента данными

Рисунок 11. Создание элемента. Заполнение элемента данными

 

C++. Вставка элемента всередину непустого списка. Шаг 2. Поиск элемента следующего перед вставляемой позициейРисунок 12. Вставка элемента в список. Поиск элемента, который следует перед позицией вставки

C++. Вставка элемента всередину непустого списка. Шаг 3. Установка указателя nextРисунок 13. Вставка элемента в список. Установка указателя next вставляемого элемента

C++. Вставка элемента всередину непустого списка. Шаг 4. Установка указателя next элемента, следующего перед позицией вставкиРисунок 14. Вставка элемента в список. Установка указателя next элемента, следующего перед позицией вставки

 

5. Удаление элемента из списка в заданной позиции

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

При удалении элемента из списка рассматриваются 2 возможные ситуации:

  • удаляется первый элемент из списка;
  • удаляется не первый элемент из списка (второй, третий и т.д.).

 

5.1. Список не пуст. Удаление первого элемента из списка

Если удаляется первый элемент в списке, то последовательность шагов удаления следующая.

1. Получить адрес первого элемента (рисунок 15)

Element<T>* delElem = begin;

2. Переместить указатель на начало списка begin на следующий элемент

begin = begin->next;

3. Освободить память, выделенную для элемента delElem

delete delElem;

На рисунках 15, 16, 17 изображен весь процесс удаления первого элемента из списка.

C++. Удаление первого элемента из списка. Шаг 1. Создать указатель на первый элементРисунок 15. Удаление первого элемента из списка. Шаг 1. Создать указатель на первый элемент

C++. Удаление элемента из списка. Шаг 2. Смещение указателя начала списка на следующий элементРисунок 16. Удаление элемента. Шаг 2. Перемещение указателя начала списка на следующий элемент

C++. Удаление первого элемента из списка. Шаг 3. Освободить память выделенную под элементРисунок 17. Удаление элемента. Шаг 3. Освободить память, выделенную под элемент

 

5.2. Список не пуст. Удаление не первого элемента из списка

Если в непустом списке удаляется не первый элемент (второй, третий и т.д., последний), то последовательность шагов следующая.

1. Переместить указатель на позицию, следующую перед удаляемым элементом. Получить элемент, предшествующий удаляемому элементу

Element<T>* elemPrev = Move(index - 1);

здесь Move() – метод, возвращающий элемент по указанной позиции.

2. Запомнить удаляемый элемент

Element<T>* elemDel = elemPrev->next;

3. Сместить указатель next предыдущего элемента elemPrev в обход удаляемого элемента elemDel

elemPrev->next = elemDel->next;

4. Удалить элемент elemDel (освободить память, выделенную для элемента).

delete elemDel;

На рисунках 18, 19, 20, 21 схематически рассмотрены вышеприведенные шаги.

C++. Удаление элемента всередине списка. Шаг 1. Переход на элемент, следующий перед предыдущимРисунок 18. Удаление элемента всередине списка. Переход на элемент, следующий перед предыдущим

C++. Удаление элемента всередине списка. Шаг 2. Получить удаляемый элементРисунок 19. Удаление элемента. Получить удаляемый элемент

C++. Удаление элемента всередине списка. Шаг 3. Смещение указателя next предыдущего элемента в обход удаляемого элементаРисунок 20. Удаление элемента из списка. Смещение указателя next предыдущего элемента в обход удаляемого элемента

C++. Удаление элемента всередине списка. Шаг 4. Освобождение памяти занятой под элемент

Рисунок 21. Удаление элемента из списка. Особождение памяти, занимаемой элементом

 


Связанные темы