Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
53
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

2. Линейные структуры данных

К линейным структурам данных относятся различные виды линейных списков.

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

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

Далее будет рассмотрено два основных подхода к обработке линейных списков — итеративная (с использованием цикла) обработка списка с выделенным текущим элементом и рекурсивная обработка списка. При изложении первого подхода отдельно рассмотрим однонаправленные и двунаправленные списки (Л1-списки и Л2-списки [12]), а также особенности кольцевых списков.

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

2.1. Атд "Стек", "Очередь", "Дек"

Стек (stack) это специальный тип списка, в котором все вставки и удаления элементов выполняются только на одном конце, называемом вершиной (top). Такой метод доступа обозначается аббревиатурой LIFO (last-in-first-out — последним пришел — первым вышел).

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

  1. Создание пустого стека — операция Create;

  2. Проверка стека на наличие в нем хотя бы одного элемента — предикат (логическая функция) IsNull.

  3. Получение элемента из вершины непустого стека — операция Top;

  4. Удаление элемента с вершины непустого стека (элемент выталкивается из стека)  — операция Pop;

  5. Вставка значения х в вершину стека (элемент заталкивается в стек) — операция Push.

Операция Сreate представляет собой примитивный конструктор, операция IsNull — индикатор, Top — селектор, операции Push и Pop — модификаторы.

Хотя семантика этих операций интуитивно понятна из их неформального описания, перед тем, как начать реализацию, рассмотрим формальную спецификацию каждой операции, применив алгебраический подход [2].

Спецификацию каждой операции представим в виде:

операция: множество входных данных  множ. выходных данных

Пусть стек состоит из элементов типа α. Введем новый тип данных  Stack of α  Stack ( α ). Непустой стек обозначим как Non_null_stack, операция  пусть обозначает декартово произведение множеств (она будет использоваться в спецификации тех операций, для которых входные или выходные данные представлены в виде двух множеств).

Базовый набор операций определяется так:

  1. Create:  Stack ( α ) ;

  2. IsNull:  Stack ( α )  Boolean ;

  3. GetTop:   Non_null_stack ( α )  α ;

  4. Pop:  Non_null_stack ( α )  Stack ( α ) ;

  5. Push:   α  Stack ( α )  Stack ( α )

Дополним данное определение набором аксиом, которые справедливы для вышеуказанных операций. Пусть p — значение типа α; s —stack ( α ):

A1. IsNull ( Create ) = true ;

A2. IsNull ( Push ( p , s ) ) = false ;

A3. Top ( Push ( p , s ) ) = p ;

A4. Pop ( Push ( p , s ) ) = s ;

A5. Push ( Top ( ) , Pop ( s ) ) = s.

Часто при определении стека вместо функции Pop используют функцию, совмещающую результат действия функций Top и Pop. Обозначим такую процедуру Pop2. Тогда

Pop2:  Non_null_stack ( α )  α  Stack ( α ).

Очередь (queue) - это другой специальный тип списка, где элементы вставляются в конец списка, называемый хвостом (tail), а удаляются из начала списка, называемого головой (head). Очереди также называют "списками типа FIFO" (аббревиатура FIFO расшифровывается как first-in-first-out: первым вошел — первым вышел). Операции, выполняемые с очередями, аналогичны операциям со стеками.

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

  1. Создание пустой очереди  — операция create

  2. Проверка очереди на наличие хотя бы одного элемента — предикат (логическая функция) isnull.

  3. Вставка значения p в конец (хвост) очереди — операция enqueue;

  4. Удаление элемента из начала (головы) непустой очереди с получением его значения — операция dequeue. Эту операцию можно рассматривать и как две различные операции: head — получение первого элемента из головы очереди и собственно dequeue — удаление элемента.

Формальная спецификация операций для очереди из элементов типа α Queue of α  Queue ( α ) ) [2] выполняется аналогично спецификации стека.

  1. Create:  Null_queue ( α ) ;

  2. IsNull: Queue ( α )  Boolean ;

  3. EnQueue: Queue ( α )  α  Queue ( α );

  4. Head: Non_null_queue ( α )  α;

  5. DeQueue: Non_null_queue ( α )  Queue ( α ).

Приведем набор аксиом, справедливых для данных операций. Пусть p — значение типа α; q —queue ( α ):

A1. IsNull ( Create ) = true ;

A2. IsNull ( EnQueue q , p ) ) = false ;

A3. Head ( EnQueue ( q , p ) ) = если IsNull ( q ) то p иначе Head ( q );

A4. DeQueue ( EnQueue ( q , ) ) = если IsNull ( ) то Create 

 иначе EnQueue ( DeQueue ( q ) , p ).

Деки

Иногда используют списки, в которых вставка и удаление элементов выполняются с обоих концов. Такой список называется деком. (сокращение от английского double ended queue-очередь с двумя концами).

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

Формальную спецификацию дека можно выполнить аналогично стеку или очереди.