Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Комплект Информатика / Лабораторный практикум.doc
Скачиваний:
178
Добавлен:
22.05.2015
Размер:
6.45 Mб
Скачать

Задание

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

Порядок выполнения работы

  1. Ознакомиться с теорией.

  2. Выполнить задание (написать программу).

  3. Ответить на контрольные вопросы.

  4. Сделать вывод.

  5. Оформить отчёт о выполненной работе.

Контрольные вопросы

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

  2. В случае реализации стека в непрерывном блоке ячеек, какое условие указывает, что стек пуст?

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

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

Требования к отчету

Отчет должен содержать:

  1. Тему и цель работы.

  2. Задание.

  3. Программу на языке программирования C++.

  4. Скриншоты с результатами выполнения программы.

  5. Ответы на контрольные вопросы.

  6. Выводы о проделанной работе.

Лабораторная работа № 24.

Очереди

Цель

Формирование навыков организации данных в виде очереди на языке программирования высокого уровня.

Задание для самостоятельной подготовки

  1. Изучить основы организации данных в виде очереди на языке программирования С++[1], стр. 351-354, [2], стр.

  2. Изучить содержание и порядок выполнения лабораторной работы.

Основы теории

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

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

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

Group 209

Рисунок 1 - Очередь с указателями головы и хвоста: a – пустая очередь; б – после добавления записи А; в – после добавления записи В; г – после удаления записи А

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

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

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

Group 329

Рисунок 2 – Очередь, «ползающая» по памяти: а – очередь с записями А, В и С; б – очереди после добавления D, E и F и удаления A и B; в – вид очереди после добавления G и H и удаления C и D

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

Этот способ реализации называется циклической очередью (circularqueue), так как образуется цикл из ячеек памяти, выделенных для очереди (рис. 3). С точки зрения этой очереди последняя ячейка в блоке соседствует с первой.

Group 497

Рисунок 3 – Фактическое устройство циклической очереди, содержащей буквы от F до O (а) и ее абстрактное представление, где последняя ячейка блока «граничит» с первой (б)

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

Создание очереди описано на псевдокоде

  1. r = new (node);//создание нового узла очереди

(*r).elem = Элем; // указатель на первый

//элемент очереди

(*r).sled = NULL; //указатель на следующий

//пустой узел

Рисунок 4 - Первый элемент в очереди

  1. no = r; // указатель на начало очереди

ko = r; // указатель на конец очереди

Рисунок 5 - Настройка указателей начала и конца очереди

Итак, мы образовали очередь, состоящую из одного звена.

  1. Продолжим заполнение очереди:

r = new (node); // создание еще одного узла

(*r).elem = Элем1; // указатель на первый элемент

(*r).sled = NULL; // указатель на

//следующий(пустой) узел

Рисунок 6 - Создание нового элемента очереди

  1. Настроим указатель на конец очереди:

(*ko).sled = r; // указатель на второй узел

ko = r; // указатель на конец очереди

Рисунок 7 – Настройка указателя на конец очереди

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

Представим описанный алгоритм в виде функции на языке C++:

void POSTROENIE (node *no, node *ko)

// Построение очереди на базе однонаправленного

// линейного списка без заглавного звена:

// *no - указатель на начало очереди,

// *ko - указатель на конец очереди.

{ //открытие тела функции POSTROENIE

node *r; // указатель начала очереди

intel; // целочисленная переменная вводимых элементов

// очереди

cin>>el; // ввод элемента

if (el!=0) // в случае если введено ‘0’

{ // начало if

r = new (node); // создание узла

(*r).elem = el; // указатель на последний введенный

// элемент

(*r).sled = NULL; // указатель на пустой узел

*no = r; //указатель начала очереди равен

//последнему созданному узлу

*ko = r; //указатель хвоста очереди равен последнему

//созданному узлу

cin>>el; // ввод следующего элемента

while (el!=0) // пока он не равен нулю делаем

{ // открытие wile

r = new (node); // создание узла

(*r).elem = el; // указатель на последний введенный

// элемент

(*r).sled = NULL; // указатель на пустой узел

(*ko).sled = r; ;// присвоение указателя очереди началу

// нового звена

*ko = r; // конец очереди это созданное звено

cin>>el; // ввод следующего элемента

}// закрытие wile

} // завершение if

else // условный оператор иначе

{ r = NULL; *no = r; *ko = r;} // производим

//обнуление указателей

} //закрытие тела функции POSTROENIE

Тут же приведем функцию для просмотра содержимого очереди:

void VYVOD (node *no, node *ko)

// Вывод содержимого очереди.

// *no - указатель на начало очереди,

// *ko - указатель на конец очереди.

{ //открытие тела функции VYVOD

node *r; // указатель начала очереди

cout<< "Очередь: "; // Вывод строки на дисплей

r = *no; // узел очереди это ее начало

while (r!=NULL) // делаем пока не дойдем до конца

//очереди

{ // открытие wile

cout<<(*r).elem<<" "; // вывод элементов очереди

r = (*r).sled; //присвоение узлу очереди следующего

//элемента

} // закрытие wile

cout<<endl; //возврат каретки

}//закрытие тела функции VYVOD

Замечание. Очереди целесообразно хранить в памяти ЭВМ в виде кольцевого списка с двумя указателями (один - на начало, другой - на конец очереди):

Рисунок 8 - Представление очереди

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