- •Программирование очередей кольцевой обработки данных.
- •Организация очередей данных
- •Алгоритм кругового обслуживания
- •Программирование очереди с кольцевым буфером.
- •Void enqueue(qType); /*добавить элемент в хвост очереди*/ qType dequeue(void); /*извлечь элемент из головы очереди*/
- •Программирование кругового обслуживания
- •Контрольные задания
- •Рекомендуемая литература
Алгоритм кругового обслуживания
Очередь с круговым буфером является удобной и естественной абстрактной структурой данных для моделирования обхода элементов по кругу в условиях задачи Иосифа по алгоритму, который можно условно назвать алгоритмом кругового обслуживания. Алгоритм кругового обслуживания составляет следующая последовательность шагов:
1. Заготовить пустую очередь с размером кольцевого буфера на 1 больше, чем число обслуживаемых элементов. 2. Применяя примитив ENQUEUE соответствующее число раз, разместить номера всех обслуживаемых элементов в кольцевом буфере очереди, что эквивалентно расположению элементов по кругу. 3. Чередуя вызовы примитивов DEQUEUE и ENQUEUE необходимое число раз, добиться, чтобы в голове очереди оказался элемент с желаемым номером. 4. Инициализировать счетчик шагов по кругу значением 1. 5. Чередовать вызовы примитивов DEQUEUE и ENQUEUE при увеличении значения счетчика шагов, пока оно не достигнет заданного порогового значения. Эти действия эквивалентны обходу круга, чтобы достигнуть элемент, который нужно удалить из очереди. Исключаемый элемент будет находиться в голове очереди. 6. Используя процедуру DEQUEUE удалить элемент из головы очереди и распечатать его номер. 7. Проверить выполнение условия пустой очереди (tail=head). Если в очереди еще остались элементы, перейти на шаг 4 для продолжения анализа очередности удаления элементов. Иначе, если очередь пуста, завершить выполнение алгоритма.
//------Основные операции с очередью
#define SIZE 100 // Максимальная длина очереди
int QUEUE[SIZE]; // Массив элементов очереди
int head; // Указатели на первый элемент очереди
int tail; // Указатель на следующий свободный за последним
void Clear() { head = tail = 0;} // Очистить очередь
int Enq(int val) // Поставить в конец очереди
{
int next;
if ((next = (tail+1) % SIZE) == head)
return(0); // Переполнение очереди
QUEUE[tail] = val;
head = next;
return(1);
}
int Deq() // Взять из начала очереди
{
int val;
if (head == tail) return(0); // Очередь пуста
val = QUEUE[head++]; //
head %= SIZE; // По достижении fst==SIZE
return(val); // сбрасывается в 0
}
Программирование очереди с кольцевым буфером.
Для программирования целесообразно применить принцип инкапсуляции объектно-ориентированного программирования, сосредоточив описание структуры очереди и примитивов ее обработки в отдельном классе RingQueue. Организация класса RingQueue должна быть ориентирована на применение его в качестве базового класса для построения производных классов, обладающих спецификой обработки кольцевой очереди в различных приложениях. Класс RingQueue должен содержать защищенные (protected) компоненты-данные и общедоступные (public) компонентные методы их обработки. Это обеспечит доступ к данным класса только собственными компонентными методами или компонентными методами производного класса, но исключит возможность непосредственного несанкционированного обращения к ним из любых внешних функций программы. Чтобы исключить зависимость компонентов класса RingQueue от типа элементов очереди, при его проектировании удобно воспользоваться средствами параметризации классов и функций, поддержка которых обеспечена в любых версиях системы программирования С++, начиная с USL 3.0. Параметризация класса осуществляется с помощью шаблона (template), который декларирует абстрактные типы данных как параметры класса. Шаблон помогает компилятору сгенерировать определение конкретного класса по образу и подобию заданной схемы. Параметры класса использованы для абстрактной типизации компонент-данных или кодов возврата и аргументов компонентных методов класса.
Шаблоны функций. Часто встречаются функции, реализующие одни и те же действия для аргументов различных типов. Например, сортировка массива по возрастанию его элементов может выполняться одним и тем же методом и для данных типа int и для данных типа double. Различие состоит только в типах параметров и некоторых внутренних переменных. Подставим вместо конкретного типа данных некоторый абстрактный тип, который, в зависимости от ситуации, будет заменен на реальный тип данных - и, таким образом, мы сможем сгенерировать тот же самый код для какого угодно типа данных. В С++ включено специальное средство, позволяющее параметризовать определение функции, чтобы компилятор мог построить конкретную реализацию функции для указанного типа параметров функции. Параметризованное определение функции строится по схеме:
template < class имя_класса > Заголовок функции { /* Тело функции */ }
Имя класса является параметром и задается идентификатором, локализованным в пределах определения функции. Хотя бы один из параметров функции должен иметь тип, соответствующий этому идентификатору. Вызов функции queue ( aint, 10 ) обеспечит вызов queue для упорядочения массива целых, а вызов функции queue ( adbl , 20 ) обеспечит вызов queue для упорядочения массива с элементами типа double. http://pgworld.narod.ru/doc/inprborcpp/objborcpp/5.htm
В данном случае в шаблоне класса RingQueue может быть объявлен абстрактный тип <class QType>, обозначающий тип элементов очереди. Учитывая синтаксические особенности шаблонов классов в системе программирования С++, декларация логической структуры класса RingQueue может иметь следующий формат:
template <class QType> class RingQueue { protected: //спецификация компонент-данных
QType* qbuf: /*абстрактный тип QType является параметром из шаблона класса, размером qbuf. Конкретизация типа параметризованных компонент шаблонного класса должна быть выполнена либо в производном нешаблонном классе или при определении объекта класса*/ int qsize; //размер очереди int head; //указатель головы очереди int tail; //указатель на хвост очереди public: /*спецификация прототипов компонентных методов*/
