Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2011_12 Комп.Науки_2сем.doc
Скачиваний:
4
Добавлен:
13.09.2019
Размер:
476.67 Кб
Скачать

21.Динамические структуры данных

21.1.Данные статической структуры и данные динамической структуры

Используемые в программировании данные можно разделить на две большие группы:

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

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

    1. односвязные и многосвязные линейные списки,

    2. кольцевые списки,

    3. стеки, очереди, дэки, деревья, графы.

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

21.2.1.Структура односвязного линейного списка

Односвязные линейные списки – это данные динамической структуры, состоящие из однородных элементов, линейно связанных между собой.

Для них определены следующие действия:

  • Добавление элемента в начало (голову) списка;

  • Добавление элемента в конец (хвост) списка;

  • Вставка элемента между двумя любыми другими элементами списка;

  • Удаление любого элемента списка.

Каждый элемент списка состоит из данных и ссылки на следующий элемент списка. На первый элемент списка и тем самым на весь список указывает переменная-указатель. Последний элемент содержит пустую ссылку nil.

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

type ref =^Elem;

Elem=record

inf: integer;

next: ref

end;

Тип ref – это указатель на переменные базового типа Elem

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

Для сохранения информации о списке достаточно одной статической переменной – указателя на первый элемент этого списка (обычно его называют головой списка).

С указателем на голову списка нельзя производить никаких действий, которые могут стать причиной утери всего списка. Для работы со списком обычно заводят вспомогательный указатель, копируя в него ссылку на голову списка, например: p:=Head.

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

p – адрес текущего элемента списка;

p^ – запись из двух полей, хранящаяся по адресу p;

p^.inf – первое поле этой записи;

p^.next – второе поле этой записи, являющееся адресом следующего элемента списка;

p^.next^.inf – первое поле элемента списка, следующего за тем, на который указывает р;

p^.next^.next – второе поле элемента списка, следующего за тем, на который указывает р.

21.2.2.Основные действия над односвязными линейными списками

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

var Head,p,pred,q,pmax:ref;

x,A,tmp:integer;

Создание пустого списка

Head:=nil

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

Исходный список

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

new(q); //Выделение в динамической памяти места для нового элемента

q^.inf:=x; //Заполнение поля данных

q^.next:=Head; //Заполнение поля ссылки

Head:=q; //Присоединение нового элемента к началу списка

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

Исходный список

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

p:=Head; //Установка текущего указателя на начало списка

Head:=p^.Next;//Установка Head на элемент, следующий за первым

Dispose(p); //Освобождение памяти, которую занимал первый элемент

p:=nil;

Проходы по списку

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

a) Проход по списку с проверкой текущего элемента

p:=Head; {Установка текущего указателя на начало списка}

while (p<>nil) and (p^.inf<>A) do

begin

Oper(p); {Какая-либо операция над текущим элементом}

p:=p^.next {Переход к следующему элементу списка}

end;

Данный проход можно использовать:

  1. Для поиска определенного элемента, тогда в заголовок цикла добавляется соответствующее условие, например, (p^.inf<>A), (p^.inf>0) и т.п. Если элемент найден, p указывает на него. Если не найден, p=nil.

  2. Для выполнения какой-либо операции над элементами (вывод, суммирование, проверка на максимальность и т.д.). Тогда в тело цикла добавляются операторы или вызов подпрограммы для реализации этой операции.

b) Проход по списку с проверкой наличия элемента, следующего за текущим элементом

p:=Head;

while (p^.next<>nil) do

p:=p^.next;

После выхода из цикла p указывает на последний элемент.

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

c) Проход по списку с двумя указателями

pred:=nil; {pred указывает на предшествующий элемент}

p:=Head; {p – на текущий элемент}

while (p<>nil) and (p^.inf<>A) do

begin

Oper(p);{Какая-либо операция над элементами списка}

pred:=p;

p:=p^.next{Переход к следующему элементу списка}

end;

Данный проход можно использовать:

  1. Для поиска определенного элемента, тогда в заголовок цикла добавляется соответствующее условие, например, (p^.inf<>A), (p^.inf>0) и т.п. Если элемент найден, p указывает на него, pred – на предшествующий. Если не найден, p=nil, pred указывает на последний элемент.

  2. Для выполнения какой-либо операции над текущим и предшествующими элементами (проверка на возрастание и т.д.). Тогда в тело цикла добавляются операторы или вызов подпрограммы для реализации этой операции.

d) Проход по списку с двумя указателями и с проверкой наличия элемента, следующего за текущим

pred:=nil; {pred указывает на предшествующий элемент}

p:=Head; {p – на текущий элемент}

while (p^.next<>nil)do

begin

pred:=p;

p:=p^.next{Переход к следующему элементу списка}

end;

После выхода из цикла p указывает на последний элемент, pred – на предшествующий элемент.

Данный проход можно использовать для удаления последнего элемента.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]