Линейные
списки, изучение основных операций
Списком называется структура данных, каждый элемент
которой посредством указателя связывается со следующим элементом. Из
определения следует, что каждый элемент списка содержит как минимуму
одно поле данных (назовем его data
и для простоты считаем его типа Integer),
оно может иметь сложную структуру, и поле ссылки на следующий элемент
(назовем его next). Поле ссылки
последнего элемента списка имеет значение Nil.
Указатель на начало списка (первый элемент) является значением отдельной
переменной. Пример списка, содержащего в поле данных целые числа 3. 5,
1, 9, приведен на рисунке.
Описание элемента списка, используемого в течение данного понятия, имеет
вид:
Основные операции с элементами списка:
-
просмотр элементов списка;
-
вставка элементов в список;
-
удаление элемента из списка;
Просмотр элементов списка, если он создан,
очевидная процедура.
|
Ее рекурсивная реализация:
|
Измените ее так, чтобы элементы списка выводились, начиная с последнего.
Вставка элемента в список возможна логически в его начало, конец и
середину. Разберем эти случаи. Вставка в начало имеет вид:
|
«Красными»
отрезками на рисунке выделены действия, выполняемые в процедуре. С
помощью цифр на рисунке и в тексте процедуры показаны действия
соответствующих операторов.
Вставка в конец списка осуществляется с
помощью следующей процедуры. Вопрос о том, каким образом значением
указателя last стал адрес последнего элемента
списка, пока оставим открытым.
Procedure Begin New(last^.next) ;(*1*) last:=last^.next;(*2*) last^.data:=el; last^.next:=Nil; End; |
Вставку в средину списка проиллюстрируем очередным рисунком.
Очевидно, что для вставки элемента в список необходимо знать, как
минимум, адрес предшествующего элемента списка, т. е. элемента, после
которого осуществляется вставка. И мы считаем, что этот элемент не
последний. При вставке разрывается существующая связь (отмечена
«крестиком») и появляются две новые связи, выделенные на рисунке
«жирными» отрезками.
Пусть мы создаем упорядоченный по неубыванию список элементов
(информационная часть любого элемента списка меньше информационных
частей следующих за ним элементов списка или равна им). Для того, чтобы
найти место для вставки очередного элемента (значение указателя рх на
рисунке), следует просматривать элементы списка до тех пор, пока
вставляемый элемент больше информационной части текущего элемента.
Procedure Var Begin New(new_m); t:=first;
While el>t^. next* .data Do new_m*.next;=tA.next; new_mA. data : =e; t*.next :=new_m; End; |
И наконец, общая логика вставки элемента в упорядоченный список.
Создаваемый список отличается от декларированного в начале тем, что он
описывается двумя указателями: на первый и последний элементы списка.
Procedure Begin
If eKfirst».data Then
If el>last».data Then End; |
Ввод исходных данных при создании упорядоченного списка осуществляется с
клавиатуры. Напомним, что признак конца файла (Eof = True) в этом случае
вырабатывается при нажатии клавиш Ctrl+Z. Все действия по организации
ввода представлены в процедуре Solve.
Procedure Var Begin Write(‘First element: ‘); Read(el) ; If Not Eof Then Begin first:~Nil; Ins_begin(first,el); last:=first; End Else Begin first:=Nil; last:=Nil; End; While Not Eof Do Begin Write(‘Next: ‘); Read(el);
If Not Eof Then End; End; |
Проведите несколько экспериментов и проанализируйте результаты:
Действия при удалении элемента из списка различны в зависимости от места
удаляемого элемента: первый он или нет. На рисунке показано удаление
элемента из средины списка. Для сохранения структуры списка необходимо
помнить адрес элемента списка, предшествующего удаляемому элементу (dx),
а также запоминать адрес удаляемого элемента для корректного
использования процедуры Dispose.
Приведем процедуру удаления всех элементов списка, информационная часть
которых равна заданному числу (el).
Procedure Var t,x,dx:pt; Begin t:=first;(*Переменная цикла. *)
While tO
If t^.data=el If t=first Then Begin (*Удаляем первый элемент списка.*) х:=first;
First:=first^.next;{*Изменяем Dispose(х); t:=first; (*Переменная цикла изменила свое значение.*) End Else Begin x:=t; (*Запоминаем адрес удаляемого элемента.*) t:=t^.next; dx^.next:=t; Dispose(x); End Else Begin dx:=t; t:=t^.next;
End; (*Переход |
Отладка
программ, использующих указатели, достаточно непростое занятие, особенно
при первом знакомстве. Рекомендуется создавать небольшой список,
например из четырех элементов 1, 2, 3, 4, и в окне Watches отслеживать
изменение всех связей. При отладке процедуры Del_el в пошаговом режиме
окно Watches может иметь в начальный момент времени следующий вид.
Watches | |
t^. data | 1 |
t^.next^.data | 2 |
t^.next^, next^, data | 3 |
t^.next^.next^.next^.data | 4 |
first^.data | 1 |
firstt^.next^.data | 2 |
firstt^.next^.next^.data | 3 |
first».next».next».next».data | 4 |