Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Уровни описания структур данных.docx
Скачиваний:
10
Добавлен:
22.09.2019
Размер:
62.72 Кб
Скачать
  1. Линейный список. Функциональная спецификация.

Линейный список это частный случай нелинейного списка (графы, деревья, и тп). Линейный список – это представление в ЭВМ конечного упорядоченного динамического мультимножества элементов типа Т. Отношений порядка на этом множестве может быть как одно, так и два. В первом случае список является односвязным, во втором двусвязным. Возможно также закольцевать список, тогда его хвост будет указывать на его голову. ЛС следует использовать когда встречаются упорядоченные множества переменного размера, где включение, поиск и удаление элементов должны выполняться не только в голове либо в хвосте, в произвольных последовательно достигаемых местах с сохранением порядка следования остальных элементов. Для поиска элемента в списке необходимо просматривать его с начала, сравнивая искомое значение с текущим элементом списка. Цена этой операции O(N). Так же удаление и вставка элемента в список будет требовать O(N).В двусвязном списке проще чем в односвязном выполняется вставка элемента до заданного. Для добавления элемента нужно указать до или после какого элемента будет производиться вставка. Если же необходимый элемент не задан, то вставка должна произвестись в конец списка.

Функциональная спецификация двусвязного списка:

  1. Создать empty ->L(T)

  2. Пусто L(T) -> bool

  3. Длина L(T) -> N

  4. Первый L(T) -> T

  5. Последний L(T) -> T

  6. Следующий L(T) * T -> T

  7. Предыдущий L(T) * T -> T

  8. Вставка L(T) * T * T -> L(T)

  9. Удаление L(T) * T -> L(T)

  10. Уничтожить L(T) -> empty

Привести тесты

Следующий(Предыдущий(т)) = т

И тп.

Для кольцевых списков: Предыдущий(Первый(L)) = Последний(L)

  1. Линейные списки. Логическое описание.

Линейные списки не входят в большинство стандартных языков программирования, однако они хорошо реализованы в альтернативных языках, таких как Пролог и Лисп. Логическое описание списков в Си и паскале тесно переплетено с их физическим представлением.

Пока список не меняется, его удобно отображать на сплошной участок памяти (вектор). Такое представление удобно для фиксированных списков и для тех, добавление в которые происходит в конец. При удалении элементов из такого списка образуются дырки, учитывать которые довольно трудно, приходится сдвигать список начиная с удаленного в среднем за N/2 перестановок. Так же высока цена и вставки элемента в сплошной список. Существует несколько способов удешевления вставки и удаления элементов: создать вектор, который будет учитывать все свободные места в списке, либо ввести дополнительный список, учитывающий свободные места в основном списке. Динамические структуры позволяют ценой расхода машинных ресурсов освободить нас от неудобств сплошного представления списка на векторе.

  1. Линейный список. Физическое представление. Итераторы.

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

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

Структура итератора и элемента:

Struct iterator { struct Item {

Item* node; Item* prev;

}; Item* next;

T data;

};

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

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

Функция Равенства для итераторов проверяет, указывают ли итераторы на один и тот же элемент списка.

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

Функция Получить возвращает значение элемента на который указывает итератор

Функция Хранить позволяет записать данные в элемент списка.

  1. Линейные списки. Физическое представление (массив).

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

Структура списка:

Struct list {

Item* head;

Int size;

Item* Top;

Item data[POOLSIZE+1];

};

Сначала список пуст и весть массив data считается свободным, а элемент data[POOLSIZE] является терминатором. Свободные элементы списка связываются друг с другом. Последний свободный элемент массива не связан ни с каким другим и его полю next присваивается значение NULL. Компонента Top указывает на первый свободный элемент в списке.

Процедура создания списка связывает все компоненты списка ссылками друг на друга. Поле head указывает на терминирующий элемент, ссылки на предыдущий и следующий элементы head тоже устанавливаются как терминирующий элемент. Полю Top присваивается ссылка на первый элемент в массиве data. Размер списка устанавливается равным нулю.

Функция вставки вставляет элемент на позицию Top, однако если Top является концом списка, то вставки не происходит и функция возвращает итератор на элемент за концом списка. В противном случае итератор на Тор запоминается во временной переменной res, далее переменной Top присваивается значение Top->next; переменной Res присваиваются значение вставляемого элемента, указателя на следующую и предыдущую переменную (от Top). Предыдущему и следующему элементам от вставляемого в соответствующие поля записываются указатели на новый элемент.

Удаление тоже очень объемно и трудно 

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

  1. Линейный список. Физическое представление (динамические структуры)

В двунаправленном списке каждый элемент содержит 2 ссылки: на предыдущий и последующий элементы относительно этого элемента. Создадим фиктивный элемент списка – терминатор, Указатель которого next будет указывать на первый элемент списка, а last на последний значащий элемент в списке (если есть, в противном случае на терминатор). Этот подход позволяет сократить количество указателей, формирующих список до 1. Посредством этой ссылки осуществляется любой доступ к списку (последовательно).

Описание структуры списка состоит из 2х полей: head и size

Struct list {

Item* head;

Int size;

};

Функция Первый возвращает итератор, указывающий на элемент поля next терминатора

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

Функция Пустоты сравнивает итераторы на начало и конец списка, пустота списка будет, если они одинаковы.

Функция Размер возвращает размер списка, который берется из структуры. Можно также узнавать размер списка перебором всех его компонент, однако это ресурсоемко.

Функция Вставки вставляет элемент перед заданным. Функция запрашивает память у ОС и в случае успеха создает итератор указывающий на новый элемент. Сам элемент заполняется переданным значением, также в него записываются ссылки на предыдущий за переданным итератором элемент и на сам итератор. У предыдущего элемента ссылка next будет указывать на созданный элемент, так и ссылка prev у переданного итератора. После увеличиваем размер списка на 1.

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

Функция уничтожения списка удаляет все элементы при помощи функции удаления элемента, пока список не пуст.