Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТСД. Лекции Силантьевой / Лекция_27_09(2)_10.doc
Скачиваний:
64
Добавлен:
10.02.2015
Размер:
324.1 Кб
Скачать

Нелинейные списки

Односвязный список всегда линейный. Двусвязный список может быть и нелинейным, то есть, II указатель двусвязного списка задает произвольный порядок следования. Таким образом, каждый элемент этого списка содержится в двух односвязных списках, при этом переменные S1 и S2 являются указателями начала двух разных односвязных списков, например,

S1

S2

D

D

A1

0

D

A1

A2

D

A1

0

D

0

A2

A1

A2

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

Если соединить входы и выходы в каждом списке, то получится нелинейная кольцевая структура.

Многосвязные списки

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

Такие списки еще называют прошитыми.

Например, есть списки абитуриентов, содержащие общие сведения: фамилию, имя и отчество, год рождения и т.д. Приемную комиссию дополнительно интересует:

    1. Москвич или нет (нуждается ли в общежитии);

    2. Медалисты;

    3. Отличники при сдаче вступительных экзаменов.

Рассмотрим пример такого списка:

Д

Данные

ескриптор

о

ОС

бщего списка

S1

Москв.

адрес начала 1

100

5

2

кол - во эл - ов 3

1

Мед.

№ ук-ля списка 4

Д

Данные

ескриптор

с

ОС

писка москвичей

S2

35

2

М

5

Мед.

Д

ОС

М

5

Мед.

Д

ОС

М

5

Мед.

ОС

М

5

Мед.

Дескриптор

списка медалистов

S3

14

4

н

е

л

и

н

Дескриптор е

отличников й

S4

н

18

3

Д

ОС

М

5

Мед.

ы

й

с

Д

п

и

с

о

к

Пример различного представления связного списка (различными структурами)

Дерево: ссылки:

А

3

родитель

1

Д

0

С

сыновья

2

В

F

0

0

G

Е

0

братья

Преобразование в бинарные

братья

дети

Наиболее общий вид многосвязной структуры характеризуется следующими свойствами:

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

    2. С каждым элементом может связываться произвольное число других элементов;

    3. Каждая связка имеет не только направление, но и вес.

Такую структуру еще называют сетью, логически она эквивалентна взвешенному ориентированному графу общего вида, и поэтому вместо термина «сеть» часто употребляется термин «графовая структура», а вместо элемента – «узел». Сетевые структуры используются в организации банковского дела, СУБД, программном имитационном моделировании, системах искусственного интеллекта, где они отражают логику организации данных и сложные отношения, возникающие между элементами данных.

Сравнение реализаций списков:

    1. Если невозможно заранее узнать количество элементов в списке, то реализовать его лучше посредством указателей;

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

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

    4. Массивы – расточительное выделение памяти - сразу под весь массив, Указатели – дополнительная память в каждом элементе на указатель.

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

Выделение и освобождение динамической памяти

Рассмотрим возможности использования динамической памяти, реализованной в Турбо-Паскале. Вся динамическая память рассматривается как сплошной массив байтов, который называется кучей (HEAP). Физически куча располагается в старших адресах сразу за областью памяти, которую занимает программа.

Системная область

Старшие адреса памяти

Куча

Программа

Системная область

HeapEnd

HeapOrg

С

HeapPtr

тандарт-

ные пере-

менные Младшие адреса памяти

Обращение к процедурам New и Delete обычно приводит к ячеистой структуре (фрагментации).

Все операции с кучей идут под управлением специальной программы – администратора кучи. Она автоматически подключается к Вашей программе компоновщиком Турбо-Паскаля и ведет учет всех свободных фрагментов в куче. Администратор кучи обрабатывает запросы New, Getmem, Dispose, Freemem и изменяет значения указателей HeapPtr и FreeList.

Системная область

Старшие адреса (ПЗУ,

BIOS, Basic, расш. BIOS)

Куча

Next

Size = 0

TFreeRec

Программа

Системная область

Младшие адреса

На этом рисунке HeapPtr – адрес нижней границы свободной кучи;

FreeList – адрес описателя первого свободного блока памяти, который указывает на структуру:

Type

PFreeRec = ^TFreeRec;

TFreeRec = Record

Next : Pointer;

Size : Pointer;

End;

Эта структура используется для описания всех свободных областей памяти, расположенных ниже границы HeapPtr, т.е появилась фрагментация. Поле Next содержит адрес описателя следующего по списку свободного блока кучи или адрес, совпадающий с HeapEnd, если этот участок последний в списке. Поле Size содержит либо ненормированную длину свободного блока, либо 0, если ниже HeapPtr нет свободных участков.

Ненормированная длина определяется таким образом:

в старшем слове содержится количество свободных параграфов (§ –16 байт), в младшем – количество свободных байт от 0 до 15.

Функция, преобразующая ненормированную длину свободного блока в байты, выглядит следующим образом:

Function BlockSize (Size : Pointer) : Longint;

Type

PtrRec = Record

Lo : Word; {свободные байты}

Hi : Word; {свободные §-фы }

End;

Var

LenghtBlock : Longint;

Begin

BlockSize := Longint(PtrRec(Size).Hi * 16 + PtrRec(Size). Lo);

End;

Сразу после загрузки получим: HeapPtr = FreeList = HeapOrg. При этом в первых 8 байтах кучи хранится запись типа TFreeRec, а Next = HeapEnd, Size = 0. При освобождении памяти уменьшится значение HeapPtr, FreeList начнет ссылаться на него и в его начале будет запись TFreeRec. Используя FreeList как начало списка, администратор кучи всегда может просмотреть весь список и при необходимости модифицировать его. Так как в любой свободный блок помещается описатель блока, равный 8 байтам, то он (блок) не может быть меньше 8 байт, даже если программа запросит 1 байт. Это надо учитывать для того, чтобы минимизировать возможные потери динамической памяти. Если же администратор кучи не может найти свободный участок памяти, то идет обращение к функции, адрес которой содержится в переменной HeapError.

В других языках программирования возможен другой подход к распределению и освобождению памяти, например, аналогично нуль-терминальным строкам: операторы присваивания реализуются путем изменения указателе и тогда освобождение памяти под переменную не гарантирует не использование ее (памяти). Для возможности ее освобождения используется счетчик обращений (К) к данному адресу, при К=0 память освобождается. Или еще подход – выделение памяти идет до тех пор, пока память есть в наличии, освободившаяся память помечается, но остается недоступной. Когда свободной памяти не остается, то инициализируется программа чистки памяти, которая собирает все ненужные ячейки, создавая список свободного пространства. При работе с динамической памятью самый плохой вариант, когда остаются недоступные участки, так как утерян адрес, ссылающийся на них:

b

a

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