Краткие сведения из теории. Работа с динамической памятью
Лабораторная работа № 7
Работа с динамической памятью.
Цель работы
Овладеть практическими навыками работы с односвязными и двусвязными списками. Научиться формировать списки, освоить технику программирования операций над списками.
Краткие сведения из теории
Односвязные списки относятся к динамическим структурам данных, которые характеризуются следующим:
1) память под данные выделяется по мере необходимости;
2) из этой структуры можно исключить элемент, который по тем или иным условиям задачи в дальнейшем не будет востребован. Элементом (узлом, звеном) списка является структура, в которой обязательно присутствует поле указателя на эту структуру. Односвязный список представляет собой множество таких узлов. При этом каждый элемент данного списка содержит адрес следующего элемента – ссылку на следующий элемент списка.
Чтобы сформировать список, надо следовать такому алгоритму:
• определить структуру, в которой обязательно присутствует поле указателя на эту структуру;
• определить некоторый набор переменных, необходимых для работы программы;
• сформировать новый элемент структуры;
• инициализировать поля нового элемента;
• если список пуст, то объявить этот элемент головным (или последним) элементом списка (в зависимости от задачи);
• если список не пустой, то новый элемент присоединить к списку;
• новый элемент списка объявить последним.
Определим структуру следующим образом:
struct node
{
int inf; // Поле для записи целых чисел.
node *next; // Поле указателя для записи адресов элементов типа node.
};
Для формирования списка нам потребуются три переменные типа node. Переменная fr – для адреса первого элемента списка; переменная r – для адреса нового узла списка; переменная er – для адреса последнего узла списка. При объявлении переменных node *fr = NULL, *r, * er; выделяется три области памяти fr, r, er для адресов переменных типа node.
Для формирования первого элемента списка надо использовать код r = new node.
После того как создан первый элемент списка, его надо инициализировать, т. е. заслать информацию в поля inf и node нового объекта. Код r -> inf = a в поле inf первого элемента списка пересылает значение из переменной a.
При выполнении кода r -> next = NULL в поле указателя next первого элемента засылается значение NULL.
Далее при следующем выполнении кода r = new node выделится новая область памяти для нового (второго) элемента списка типа node. При выполнении директивы r -> inf = a в поле inf второго элемента списка пересылается значение из переменной a. При выполнении директивы r -> next = NULL в поле указателя next второго элемента засылается значение NULL.
Теперь нам надо присоединить новый (второй) сформированный узел списка к предыдущему (первому) узлу списка. Это достигается выполнением кода:
er -> next = r.
Удаление элемента из списка. Решение данной задачи зависит от расположения элемента в списке. Способ удаления головного (первого) элемента списка отличается от способа удаления любого другого элемента.
Для того чтобы удалить первый элемент списка, надо выполнить следующие директивы:
r = fr;
fr = fr -> next;
delete r;
Напомним, что директива delete r освобождает использованный программой участок памяти, адрес которого содержался в указателе r.
Для того чтобы удалить элемент со значением b ≠a1, надо:
• найти элемент списка, для которого b = ak;
• при поиске сохранить в указателе значение адреса на предыдущий элемент списка.
Для того чтобы удалить элемент списка со значением ak = b, необходимо выполнить следующие две директивы:
rp->next = r->next;
delete r;
Двусвязный список есть динамическая структура данных (ДСД). Каждый его узел связан как с предыдущим, так и с последующим элементом.
Структура для элемента двусвязного списка определяется следующим образом:
struct node
{ int inf;
node *lt;
node *rt;
};
Приведем фрагмент программы для формирования первого элемента списка. Смысл параметров, которые используются во фрагменте, следующий: r – указатель типа node для формирования нового узла списка, или указатель на текущий (новый) элемент списка; fr – указатель типа node для адреса последнего элемента списка, или указатель на последний элемент списка; en – указатель типа node для адреса последнего элемента списка, или указатель на последний элемент списка.
r = new node; // Выделяется память под первое звено списка.
// Адрес звена засылается в указатель r.
r -> lt = NULL; // Определяется поле указателя lt первого звена.
r -> rt = NULL; // Определяется поле указателя rt первого звена.
r -> inf = nz; // Определяется поле inf первого звена.
fr = r; // Определяется указатель на первое звено списка.
en = r; // Определяется указатель на последнее звено списка.
Приведем фрагмент программы, который демонстрирует, как новый элемент присоединяется к существующему списку. Каждый код фрагмента поясняется в комментариях. Смысл параметров, которые используются во фрагменте, следующий: r – указатель типа node для формирования нового узла списка, или указатель на текущий (новый) элемент списка; en – указатель типа node для адреса последнего элемента списка, или указатель на последний элемент списка.
r = new node; // Выделяется память под новое текущее звено.
// Адрес нового звена заносится в указатель r.
r -> inf = nz; // В поле inf нового узла засылается его значение.
r -> rt = NULL; // Правый указатель нового звена должен иметь
// значение NULL.
r -> lt = en; // К новому узлу присоединяется сформированная
// часть двусвязного списка. Для этого в левый
// указатель нового узла пересылаем адрес на
// последний элемент списка.
// Заметим, что по отношению к новому узлу эта часть
// является левой частью списка.
en -> rt = r; // К списку присоединяем новый узел. Для этого в правый
// указатель последнего элемента списка засылаем адрес
// нового звена.
en = r; // Новое звено объявляем последним.
Порядок выполнения работы