Объектно-ориентированное программирование - Учебное пособие (А.А. Хусаинов)

3.2. параметризованные очереди и стеки

 

Очередь представляет собой линейный список, доступ к элементам которого осуществляется по принципу FIFO («первым вошел – первым вышел»).

Рассмотрим две функции обслуживания очереди: qstore() и qretrieve(). Функция qstore() помещает элемент в конец очереди, а функция qretrieve() извлекает из очереди первый элемент и возвращает его значение.

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

 

// очередь элементов типа Qtype

#include <conio.h>                    //библиотека консольного ввода-вывода

#include <iostream.h>   //библиотека потокового ввода-вывода

#include <stdlib.h>                   //стандартная библиотека

 

template <class Qtype> class queue

{

   Qtype *q;         // массив элементов очереди

   int sloc, rloc;   // последний записанный элемент

                                       // и последний прочитанный

   int length;       // размер очереди

public:

   queue(int size);            // конструктор

   ~queue() { delete [] q;} //деструктор

   void qstore(Qtype i); //запись в конец очереди

   Qtype qretrieve(); // получение первого элемента очереди

};

// конструктор

template <class Qtype>

queue <Qtype>:: queue(int size)

{

   size++; // размер на 1 больше

   q = new Qtype[size]; //выделим память

   if(!q) //если не удалось выделить память

   {

               cout << " нет памяти для очереди"; exit(1);

}

length = size;   //длина очереди          

sloc = rloc = 0; // начало и конец очереди

}

// запись в очередь

template <class Qtype>

void queue <Qtype>:: qstore(Qtype i)

{

   if(sloc+1 == length) //если нельзя сместить указатель на конец

   {

               cout << "Очередь переполнена ";

               return;

}

sloc++; //смещаем указатель

q[sloc] = i; //записываем элемент

}

// чтение и удаление из очереди

template <class Qtype>

Qtype queue <Qtype> :: qretrieve()

{

   if(rloc == sloc) //если указатель на конец равен указателю на начало

   {

               cout << " Очередь пуста ";

               return 0;

}

rloc++; return q[rloc];

}

 

// пример использования очереди

main()

{

   clrscr();                                    //очистка экрана

   queue <int> a(10), b(10); //две очереди с целыми числами

   queue <double> c(10);            //одна очередь с числами с плавающей точкой

   a.qstore(100);

   a.qstore(200);

   b.qstore(300);

   b.qstore(400);

   cout<<"Первый элемент очереди а "<<a.qretrieve()<<" ";//выведет 100

   cout<<"Второй элемент очереди а "<<a.qretrieve()<<" "; // выведет 200

   cout << a.qretrieve() << " "; // "очередь пуста"

   c.qstore(-1);

   c.qstore(-2);

   cout<<"Первый элемент очереди с "<<c.qretrieve()<<" "; //выведет -1

   return 0;

}

 

Результат работы программы

 

Первый элемент очереди а 100

Второй элемент очереди а 200

Очередь пуста 0

Первый элемент очереди с –1

 

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

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

 

// очередь элементов типа Qtype

#include <conio.h>                    //библиотека консольного ввода-вывода

#include <iostream.h>   //библиотека потокового ввода-вывода

#include <stdlib.h>                   //стандартная библиотека

 

template <class Qtype> class queue

{

   Qtype *q;         // массив элементов очереди

   int sloc, rloc;   // последний записанный элемент

                                       // и последний прочитанный

   int length;       // размер очереди

public:

   queue(int size);            // конструктор

   ~queue() { delete [] q;} //деструктор

   void qstore(Qtype i); //запись в конец очереди

   Qtype qretrieve(); // получение первого элемента очереди

};

// конструктор

template <class Qtype>

 

queue <Qtype>:: queue(int size)

{

   size++; // размер на 1 больше

   q = new Qtype[size]; //выделим память

   if(!q) //если не удалось выделить память

   {

               cout << " нет памяти для очереди"; exit(1);

}

length = size;   //длина очереди          

sloc = rloc = 0; // начало и конец очереди

}

// запись в очередь

template <class Qtype>

void queue <Qtype>::qstore(Qtype i) // добавление

{

if((sloc+1 == rloc) || (sloc+1 == length) && !rloc)

   {

               cout << " Очередь переполнена"; return;

}

q[sloc] = i; sloc++;

if(sloc == length) sloc = 0;         // циклический список

}

// чтение и удаление из очереди

template <class Qtype>

Qtype queue <Qtype>:: qretrieve()

{

   if(rloc == length) rloc = 0;

   if(rloc == sloc)

   {

               cout << " Очередь пуста"; return 0;

}

rloc++;

return q[rloc-1];

}

 

// пример использования очереди

main()

{

   clrscr();                                    //очистка экрана

   queue <int> a(10), b(10); //две очереди с целыми числами

   queue <double> c(10);            //одна очередь с числами с плавающей точкой

   a.qstore(100);

   a.qstore(200);

   b.qstore(300);

   b.qstore(400);

   cout<<"Первый элемент очереди а "<<a.qretrieve()<<" ";//выведет 100

   cout<<"Второй элемент очереди а "<<a.qretrieve()<<" "; // выведет 200

   cout << a.qretrieve() << " "; // "очередь пуста"

   c.qstore(-1);

   c.qstore(-2);

   cout<<"Первый элемент очереди с "<<c.qretrieve()<<" "; //выведет -1

   return 0;

}

 

Результат работы программы

 

Первый элемент очереди а 100

Второй элемент очереди а 200

Очередь пуста 0

Первый элемент очереди с –1

 

Стек представляет собой линейный список, доступ к элементам которого осуществляется по принципу LIFO («последним вошел – первым вышел»).        

Две основные операции традиционно называются pop() (извлечение) и push() (добавление).        

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

 

//программа, использующая стек       

#include <conio.h>                    //библиотека консольного ввода-вывода

#include <iostream.h>               //библиотека потокового ввода-вывода

#include <stdlib.h>       //стандартная библиотека

 

template <class Stype>

class stack         //класс стека

{

   Stype *s;                     // массив, содержащий элементы стека

   int tos;                         // число записанных элементов

   int length;                    // размер стека

public:

   stack(int size);        // конструктор

   ~stack() {delete [] s;} // освобождение памяти

   void push(Stype i);     // запись элемента в стек

   Stype pop();            // извлечение элемента из стека

};

// конструктор

template <class Stype>

stack <Stype>:: stack(int size)

{

   s = new Stype[size];    // захват памяти

   if(!s)               // если s=0

   {

               cout << " Нет памяти для стека";

               exit(1);

}

length = size; tos = 0;    // вершина стека

}

// запись объекта в стек

template <class Stype>

void stack <Stype>:: push(Stype i)

{

   if(tos == length)          //если длина стека - максимум

   {

               cout << " Стек переполнен";

               return;

}

s[tos++] = i;                   //сохраняем элемент в стеке

}

// чтение и удаление объекта из стека

template <class Stype>

Stype stack <Stype>:: pop()

{

   if(tos == 0)

{

   cout << " Стек пуст"; return 0;

}

return s[--tos];                //получение элемента из стека

}

// примеры использования стека

main()

{

   stack <int>        a(10); // стек целых чисел

   stack <double> b(10);     // стек чисел с плавающей точкой

   clrscr();                                      // очистка экрана

   a.push(10);               // запись в стек a

   b.push(-1);               // запись в стек b

   a.push(20);               // запись в стек a

   cout <<"Последний элемент стека а "<< a.pop()<<" ";// вывод числа 20

   cout <<"Последний элемент стека b "<< b.pop();      // вывод числа -1

   return 0;

}

 

Результат работы программы

 

Последний элемент стека а 20

Последний элемент стека b -1