C++ 05
NEW & DELETE
ДИНАМИЧЕСКИЙ МАССИВ
ПОЛУСТАТИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ. ПРИЗНАКИ • имеют переменную длину и простые процедуры ее изменения; • изменение длины структуры происходит в определенных пределах, не превышая какого-то максимального (предельного) значения.
ЛОГИЧЕСКИЙ УРОВЕНЬ Последовательность данных, связанная отношениями линейного списка. Доступ к элементу может осуществляться по его порядковому номеру ФИЗИЧЕСКОЕ ПРЕДСТАВЛЕНИЕ Последовательность слотов в памяти, где каждый следующий элемент расположен в памяти в следующем слоте (т. е. вектор)
СТЕКИ (ОЧЕРЕДИ LIFO)
Стек - последовательный список с переменной длиной, включение и исключение элементов из которого выполняются только с одной стороны списка, называемого вершиной стека. Другие названия стека - магазин и очередь LIFO (Last - In - First- Out ) Примеры: винтовочный патронный магазин, тупиковый железнодорожный разъезд для сортировки вагонов.
ОПЕРАЦИИ НАД СТЕКОМ Основные операции над стеком: • Начальное формирование стека • включение нового элемента (push) • исключение элемента из стека (pop). Вспомогательные операции: • определение текущего числа элементов в стеке; • очистка стека; • неразрушающее чтение элемента из вершины стека, которое может быть реализовано, как комбинация основных операций: x: =pop(stack); push(stack, x).
СХЕМА СТЕКА
Пустой стек; Последовательное включение в него элементов с именами 'A', 'B', 'C'; Последовательное удаление из стека элементов 'C' и 'B'; Включение в стек элемента 'D'.
ПУСТОЙ СТЕК stk = NULL;
ФОРМИРОВАНИЕ НОВОГО ЭЛЕМЕНТА cin>>Элем; t = new (node); (*t). elem = Элем; (*t). sled = stk;
"НАСТРОЙКА" УКАЗАТЕЛЯ СТЕКА stk = t;
ПЕРВЫЙ ЭЛЕМЕНТ В СТЕКЕ
Размещение в стеке второго элемента cin>>Элем 1; t = new (node); (*t). elem = Элем 1; (*t). sled = stk;
"Настройка" указателя стека stk = t;
В стеке два элемента
ПОСТРОЕНИЕ СТЕКА void POSTROENIE (node **stk) { node *t; int el; *stk = NULL; cin>>el; while (el!=0) { t = new (node); (*t). elem = el; (*t). sled = *stk; *stk = t; cin>>el; }}
ВКЛЮЧЕНИЕ ЗВЕНА В СТЕК Исходное состояние стека
Создание нового элемента q = new (node); (*q). elem = Элем;
Включение элемента в стек (*q). sled = stk;
"Настройка" указателя вершины стека stk = q;
ДОБАВЛЕНИЕ void W_S (node **stk, int el) { node *q; q = new (node); (*q). elem = el; (*q). sled = *stk; *stk = q; }
ПОСТРОЕНИЕ void POSTROENIE (node **stk) { int el; *stk = NULL; cin>>el; while (el!=0) { W_S (stk, el); cin>>el; }}
УДАЛЕНИЕ ЗВЕНА ИЗ СТЕКА Исходный стек
СОХРАНЕНИЕ УДАЛЯЕМОГО ЭЛЕМЕНТА klad = (*stk). elem;
"ПЕРЕНАСТРОЙКА" УКАЗАТЕЛЯ СТЕКА q = stk; stk = (*stk). sled;
ОЧИСТКА ПАМЯТИ delete q;
УДАЛЕНИЕ ЭЛЕМЕНТА ИЗ СТЕКА void YDALENIE (node **stk, int klad) { node *q; if (*stk==NULL) cout<<"Стек пуст!n"; else { *klad = (**stk). elem; q = *stk; *stk = (**stk). sled; delete q; } }
ОЧЕРЕДИ (ОЧЕРЕДИ FIFO)
ЛОГИЧЕСКАЯ СТРУКТУРА ОЧЕРЕДИ Очередью FIFO (First - In - First- Out) называется последовательный список с переменной длиной, в котором включение элементов выполняется только с одной стороны списка (эту сторону часто называют концом или хвостом очереди), а исключение - с другой стороны (называемой началом или головой очереди)
ОПЕРАЦИИ НАД ОЧЕРЕДЬЮ Основные операции над стеком: • Начальное формирование стека • включение нового элемента (push) • исключение элемента из стека (pop). Вспомогательные операции: • определение текущего числа элементов в стеке; • очистка стека; • неразрушающее чтение элемента из вершины стека, которое может быть реализовано, как комбинация основных операций: x: =pop(stack); push(stack, x).
СХЕМАТИЧНОЕ ИЗОБРАЖЕНИЕ ОЧЕРЕДИ
ФОРМИРОВАНИЕ ОЧЕРЕДИ Первый элемент в очереди r = new (node); (*r). elem = Элем; (*r). sled = NULL;
НАСТРОЙКА УКАЗАТЕЛЕЙ НАЧАЛА И КОНЦА ОЧЕРЕДИ no = r; ko = r;
СОЗДАНИЕ НОВОГО ЭЛЕМЕНТА ОЧЕРЕДИ r = new (node); (*r). elem = Элем 1; (*r). sled = NULL;
НАСТРОЙКА УКАЗАТЕЛЯ НА КОНЕЦ ОЧЕРЕДИ (*ko). sled = r; ko = r;
void POSTROENIE (node **no, node **ko) { node *r; int el; cin>>el; if (el!=0) { r = new (node); (*r). elem = el; (*r). sled = NULL; *no = r; *ko = r; cin>> el; while (el!=0) { r = new (node); (*r). elem = el; (*r). sled = NULL; (**ko). sled = r; *ko = r; cin>>el; } } else { r = NULL; *no = r; *ko = r; }}
ДОБАВЛЕНИЕ ЗВЕНА К ОЧЕРЕДИ Заполнение добавляемого звена r = new (node); (*r). elem = Элем; (*r). sled = NULL;
Присоединение звена к очереди (*ko). sled = r;
"НАСТРОЙКА" УКАЗАТЕЛЯ НА КОНЕЦ ОЧЕРЕДИ ko = r;
void DOBAVLENIE (node **no, node **ko, int el) { node *r; r = new (node); (*r). elem = el; (*r). sled = NULL; if (*no!=NULL) { (**ko). sled = r; *ko = r; } else { *no = r; *ko = r; }}
УДАЛЕНИЕ ЗВЕНА ИЗ ОЧЕРЕДИ
Сохранение удаляемого элемента klad = (*no). elem;
Сохранение указателя на удаляемый элемент и “перенастройка” указателя на начало очереди q = no; no = (*no). sled;
УДАЛЕНИЕ delete q;
void YDALENIE (node **no, node **ko, int klad) { node *q; if (*no==NULL) cout<< "Удалить нельзя, так как очередь пуста!n"; else { *klad = (**no). elem; q = *no; *no = (**no). sled; delete q; } }
ОЧЕРЕДИ С ПРИОРИТЕТАМИ Порядок выборки элементов из таких очередей определяется приоритетами элементов. Приоритет в общем случае может быть представлен числовым значением, которое FIFO, и LIFO-очереди могут трактоваться как приоритетные очереди, в которых приоритет элемента зависит от времени его включения в очередь. При выборке элемента всякий раз выбирается элемент с наибольшим приоритетом. Возможны очереди с приоритетным включением - в которых последовательность элементов очереди все время поддерживается упорядоченной, т. е. каждый новый элемент включается на то место в последовательности, которое определяется его приоритетом, а при исключении всегда выбирается элемент из начала. Возможны и очереди с приоритетным исключением - новый элемент включается всегда в конец очереди, а при исключении в очереди ищется (этот поиск может быть только линейным) элемент с максимальным приоритетом и после выборки удаляется из последовательности.
ДЕКИ
Дек - особый вид очереди. Дек (deq - double ended queue) - это такой последовательный список, в котором как включение, так и исключение элементов может осуществляться с любого из двух концов списка. Частный случай дека - дек с ограниченным входом и дек с ограниченным выходом. Логическая и физическая структуры дека аналогичны логической и физической структуре кольцевой FIFO-очереди.
ОПЕРАЦИИ НАД ДЕКОМ • Формирование дека • включение элемента справа; • включение элемента слева; • исключение элемента справа; • исключение элемента слева; • определение размера; • очистка.
"НАЧАЛЬНАЯ" ПОЗИЦИЯ ДЕКА С ЗАГЛАВНЫМ ЗВЕНОМ
ИСКЛЮЧЕНИЕ ЗАГЛАВНОГО ЗВЕНА q = nd; nd = (*nd). sled; (*nd). pred = NULL;
"НАСТРОЙКА" УКАЗАТЕЛЯ И ОЧИСТКА ПАМЯТИ delete q; kd = z;
void Built. Deck (node **nd, node **kd) // Построение дека на базе двунаправленного // списка с заглавным звеном. // *nd - указатель на начало дека. // *kd - указатель на конец дека. { node *q, *z; int el; // Построение заглавного звена. *nd = new(node); z = *nd; (**nd). pred = (**nd). sled = NULL; cout<<"Введите последовательность: n"; cin>>el; while (el!=0) { (*z). sled = new(node); (*((*z). sled)). pred = z; z = (*z). sled; (*z). sled = NULL; (*z). elem = el; cin>>el; } if ((**nd). sled!=NULL) { q = *nd; *nd = (**nd). sled; (**nd). pred = NULL; *kd = z; delete q; } else { delete *nd; *nd = *kd = NULL; }}
ВСТАВКА ЗВЕНА В НАЧАЛО ДЕКА q = new(node); (*q). elem = Элем; (*q). sled = nd; (*q). pred = NULL; (*nd). pred = q; nd = q;
void Ins. Left (node **nd, node **kd, int el) // Вставка звена, содержащего элемент el, в дек слева. // *nd - указатель на начало дека. / / *kd - указатель на конец дека. { node *q; q = new(node); (*q). elem = el; if (*nd==NULL) { // Если дек пуст, то. . . *nd = q; (*q). sled = (*q). pred = NULL; *kd = q; } else { (*q). sled = *nd; (*q). pred = NULL; (**nd). pred = q; *nd = q; } }
ВСТАВКА ЗВЕНА В КОНЕЦ ДЕКА q = new(node); (*q). elem = Элем; (*q). sled = NULL; (*q). pred = *kd; (*kd). sled = q; *kd = q;
void Ins. Right (node **nd, node **kd, int el) // Добавление звена, содержащего элемент el, в дек справа. // *nd - указатель на начало дека. // *kd - указатель на конец дека. { node *q; q = new(node); (*q). elem = el; if (*kd==NULL) { // Если дек пуст, то. . . *nd = q; (*q). sled = (*q). pred = NULL; *kd = q; }else { (*q). sled = NULL; (*q). pred = *kd; (**kd). sled = q; *kd = q; } }
АЛГОРИТМ УДАЛЕНИЯ ЗВЕНА ДЕКА
"НАСТРОЙКА" УКАЗАТЕЛЕЙ q = nd; nd = (*nd). sled;
ОЧИСТКА ПАМЯТИ delete q;
void Del. Left (node **nd, node **kd, int *el) { node *q; if ((**nd). sled!=NULL) { q = *nd; *el =(*q). elem; *nd = (**nd). sled; (**nd). pred = NULL; delete q; } else { q = *nd; *el =(*q). elem; *nd = *kd = NULL; delete q; cout<<"Дек пуст!n"; } }
"НАСТРОЙКА" УКАЗАТЕЛЕЙ q = kd; kd = (*kd). pred; (*kd). sled = NULL;
delete q;
void Del. Right (node **nd, node **kd, int *el) { node *q; if ((**kd). pred!=NULL) { q = *kd; *el =(*q). elem; *kd = (**kd). pred; (**kd). sled = NULL; delete q; } else { q = *kd; *el =(*q). elem; *nd = *kd = NULL; delete q; cout<<"Дек пуст!n"; } }
ЛИНЕЙНЫЕ ОДНОНАПРАВЛЕННЫЕ СПИСКИ
ОПРЕДЕЛЕНИЯ Список - это совокупность объектов, называемых элементами списка, в которой каждый объект содержит информацию о местоположении связанного с ним объекта
ЭЛЕМЕНТ СПИСКА, ЗВЕНО информационное поле, которое в общем случае может содержать произвольное количество полей разных типов. Если значением переменной p является ссылка на элемент списка, то присоединяя к обозначению (*p) с помощью точки имя соответствующего поля, можно манипулировать со значением любого поля информационной части; ссылка на следующий элемент списка
struct node { int elem; //Информационный элемент звена списка node *sled; // Указатель на следующее звено списка }; struct node *phead;
ОДНОНАПРАВЛЕННЫЕ СПИСКИ БЕЗ ЗАГЛАВНОГО ЗВЕНА
ОПЕРАЦИИ НАД СПИСКАМИ начальное формирование списка (запись первой компоненты); добавление компоненты в конец списка; определение первого элемента в линейном списке; чтение компоненты с заданным ключом; с заданным свойством; вставка компоненты в заданное место списка (обычно до компоненты с заданным ключом или после неё); исключение компоненты с заданным ключом из списка. упорядочивание узлов линейного списка в определенном порядке.
РАЗМЕЩЕНИЕ УКАЗАТЕЛЕЙ В ПАМЯТИ struct node *phead; struct node *t;
РЕЗЕРВИРУЕТСЯ МЕСТО ДЛЯ ДИНАМИЧЕСКОГО ОБЪЕКТА phead = new (node);
ОПРЕДЕЛЕНИЕ T t = phead; (*t). value = Элем;
ДОБАВЛЕНИЕ НОВОГО ЭЛЕМЕНТА (*t). next = new (node);
T СОДЕРЖИТ АДРЕС ПОСЛЕДНЕГО ЭЛЕМЕНТА t = (*t). next;
ЗАВЕРШЕНИЕ ПОСТРОЕНИЯ СПИСКА (*t). next = NULL; (*t). value = Элем 1;
КОНЕЧНЫЙ СПИСОК #include
СПИСОК С ЗАГЛАВНЫМ ЗВЕНОМ
ДВА СПОСОБА ПОСТРОЕНИЯ Каждый новый элемент добавляется в начало или в конец списка. Каждый элемент добавляется в свое место в списке, например, так, чтобы обеспечить упорядоченность по возрастанию или убыванию.
РЕЗЕРВИРОВАНИЕ МЕСТА В ПАМЯТИ struct node *phead; struct node *t;
ВЫДЕЛЕНИЕ МЕСТА ДЛЯ ЭЛЕМЕНТА СПИСКА phead = new(node);
ПРИСВОЕНИЕ ЗНАЧЕНИЯ T t = phead;
ПРИСВОЕНИЕ УКАЗАТЕЛЮ ЗАГЛАВНОГО ЗВЕНА NULL (*t). sled = NULL;
СОЗДАНИЕ НОВОГО ОБЪЕКТА cin>>Число; while (Число != Числу, определяющему окончание ввода) { (*t). sled = new (node); //Резервируем место для нового объекта.
ОПРЕДЕЛЯЕМ T t = (*t). sled; //Указатель t содержит адрес //расположения созданного объекта.
РЕЗУЛЬТАТ ЗАПОЛНЕНИЯ ПОЛЕЙ (*t). elem = Число; //Заполняем поля объекта. (*t). sled = NULL;
ОКОНЧАНИЕ cin>>Число; //Запрос на ввод следующего значения. }
void POSTROENIE (node **phead) // Построение списка с заглавным звеном. // *phead - указатель на заглавное звено. { node *t; int el; // Вначале создадим заглавное звено *phead = new (node); t = *phead; (*t). sled = NULL; cout<<"Вводите элементы звеньев списка: "; cin>>el; while (el!=0) { (*t). sled = new (node); t = (*t). sled; (*t). elem = el; (*t). sled = NULL; cin>>el; }}
ПУСТОЙ ОДНОНАПРАВЛЕННЫЙ СПИСОК С ЗАГЛАВНЫМ ЗВЕНОМ
УДАЛЕНИЕ СПИСКА ИЗ ПАМЯТИ
вводятся два указателя q и q 1, один из которых будет "опережать" другой (пусть q 1 опережает q); начальное значение q - это адрес расположения в памяти заглавного звена, а q 1 - адрес расположения первого элемента списка; цикл, пока q 1!=NULL, то есть пока q 1 "не доберется" до указателя последнего элемента списка; в теле цикла переместим указатели на следующую пару элементов списка (то есть для первого случая q будет содержать адрес первого звена списка, а q 1 - второго), и удалим элемент, который адресуется значением q; после выполнения цикла у нас останется только заглавное звено, адресуемое указателем phead. Его также нужно удалить.
void OCHISTKA (node **phead) //Удаление однонаправленного списка из памяти. // *phead - указатель на заглавное звено списка. { struct node *q, *q 1; // Рабочие указатели. q = *phead; q 1 = (*q). sled; // Указатель q 1 "опережает" указатель q. while (q 1!=NULL) { q = q 1; q 1 = (*q 1). sled; delete q; } delete *phead; //Удаление заглавного звена. }
ПОИСК ЗВЕНА void POISK (node **phead, int el, node **Res) // Поиск звена с элементом el в списке, заданном указателем *phead. // В случае успешного поиска в *Res находится адрес // звена списка, содержащего элемент el, в случае неуспеха в *Res помещается NULL. { node *t; *Res = NULL; t = *phead; t = (*t). sled; while (t!=NULL && *Res==NULL) if ((*t). elem==el) *Res = t; else t = (*t). sled; }
ВКЛЮЧЕНИЕ ЗВЕНА 1 -Й СЛУЧАЙ. ПОСЛЕ ЗВЕНА, НА КОТОРОЕ УКАЗЫВАЕТ ЗАДАННАЯ ССЫЛКА.
РЕЗЕРВИРУЕТСЯ МЕСТО ДЛЯ ДИНАМИЧЕСКОГО ОБЪЕКТА q = new (node);
ЗАПОЛНЕНИЕ ИНФОРМАЦИОННОГО ПОЛЯ (*q). elem = el;
ЗАПОЛНЕНИЕ ПОЛЯ УКАЗАТЕЛЯ ВСТАВЛЯЕМОГО ЭЛЕМЕНТА (*q). sled = (*Res). sled;
ИЗМЕНЕНИЕ УКАЗАТЕЛЯ У ПРЕДЫДУЩЕГО ЭЛЕМЕНТА (*Res). sled = q;
void VSTAV (node **Res, int el) // Включение звена с информационным полем el //после звена, на которое указывает ссылка *Res. { node *q; q = new (node); (*q). elem = el; (*q). sled = (**Res). sled; (**Res). sled = q; }
ВКЛЮЧЕНИЕ ЗВЕНА 2 -Й СЛУЧАЙ. ПЕРЕД ЗВЕНОМ, НА КОТОРОЕ УКАЗЫВАЕТ ССЫЛКА.
ВЫДЕЛЕНИЕ МЕСТА В ПАМЯТИ ДЛЯ НОВОГО ЗВЕНА q = new (node);
ЗАПОЛНЕНИЕ ПОЛЕЙ НОВОГО ЭЛЕМЕНТА (*q). elem = (*Res). elem; (*q). sled = (*Res). sled;
ВКЛЮЧЕНИЕ ЭЛЕМЕНТА В СПИСОК (*Res). elem = el; (*Res). sled = q;
void VSTAV 1 (node **Res, int el) //Включение звена с информационным полем el //перед звеном, на которое указывает *Res. { node *q; q = new (node); (*q). elem = (**Res). elem; (*q). sled = (**Res). sled; (**Res). elem = el; (**Res). sled = q; }
УДАЛЕНИЕ ЗВЕНА, РАСПОЛОЖЕННОГО ПОСЛЕ ЗВЕНА, НА КОТОРОЕ УКАЗЫВАЕТ ССЫЛКА. //Определение местоположения удаляемого звена q = (*Res). sled;
ИСКЛЮЧЕНИЕ УДАЛЯЕМОГО ЭЛЕМЕНТА ИЗ СПИСКА if (q!=NULL) //Если звено, после которого нужно удалять, // не является последним, то. . . { (*Res). sled = (*(*Res). sled;
ОЧИСТКА ПАМЯТИ delete q; }
void YDALE (node **Res) // Удаление звена, расположенного после // звена, на которое указывает ссылка *Res. { node *q; q = (**Res). sled; if (q!=NULL) // Если звено, после которого нужно удалять, // не является последним, то. . . { (**Res). sled = (*(**Res). sled; delete q; } else cout<<"Звено с заданным элементом - последнее!n"; }
УДАЛЕНИЕ ЗВЕНА, НА КОТОРОЕ УКАЗЫВАЕТ ССЫЛКА //Определяется местоположение следующего за удаляемым элемента q = (*Res). sled;
"ПЕРЕПИСЫВАТСЯ" СЛЕДУЮЩИЙ ЭЛЕМЕНТ В УДАЛЯЕМЫЙ if (q!=NULL) { //Если удаляемое звено не является последним, то. . . (*Res). elem = (*q). elem; (*Res). sled = (*q). sled;
ОЧИСТКА ПЯМЯТИ delete q; }
УДАЛЯЕМЫЙ ЭЛЕМЕНТ - ПОСЛЕДНИЙ Найдем указатель на предпоследнее звено линейного списка с помощью двух вспомогательных указателей q 1 иq 2, перемещающихся по списку "параллельно другу", причем указатель q 2 "опережает" указатель q 1 на один шаг.
ПЕРЕД УДАЛЕНИЕМ q 1 = phead; q 2 = (*q 1). sled; //Инициализация указателей. while (q 2!=Res) { q 1 = q 2; q 2 = (*q 2). sled; }
ПОСЛЕ УДАЛЕНИЯ (*q 1). sled = NULL; q 2 = NULL; delete (Res);