Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Классы коллекций для практики.doc
Скачиваний:
9
Добавлен:
05.11.2018
Размер:
397.31 Кб
Скачать

4.5. Базовый класс последовательного списка

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

О

перации базового списка включают вставку нового элемента в конец списка, удаление элемента, доступ к элементу в списке по позиции и очистку списка. Имеются также операции для тестирования, является ли список пустым, или находится ли какой-либо элемент в списке. В качестве примера этому из реальной жизни рассмотрим список продуктов для покупки в универсаме (Рис.4.6). Когда вы идете по универсаму, вы решаете купить дополнительные товары и добавляете их в конец списка. Когда товар найден, вы удаляете его из списка. Список с этими простыми операциями может использоваться для решения существенных задач. Приложение описывает отдел видеотоваров, в котором ведется список имеющихся фильмов и список клиентов. Когда какой-либо фильм выдается клиенту, он переходит из списка имеющихся фильмов в список клиентов. При возврате фильма происходит обратный процесс.

Список ADT описывает однородные списки (homogeneous lists), в которых каждый элемент имеет один и тот же тип данных, называемый DataType. В определении ADT не упоминается о том, как хранятся элементы. Для этого может использоваться массив или связанный список с применением динамического распределения памяти. Реализации операций Insert, Delete и find зависят от метода, используемого для хранения элементов

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

Спецификация класса SeqLlst

ОБЪЯВЛЕНИЕ

#include <iostream.h>

#include <stdlib.h>

typedef int DataType;

const int MaxListSize = 50;

class SeqList

{

private:

// массив для списка и число элементов текущего списка

DataType listitem[MaxList3ize];

int size;

public:

// конструктор SeqList(void) ;

// методы доступа

int ListSize(void) const;

int ListEmpty(void) const;

int Find (DataTypes item) const;

DataType GetData(int pos) const;

// методы модификации списка

void Insert(const DataTypes item);

void Delete(const DataTypes item);

DataType DeleteFront(void);

void ClearList(void);

};

ОПИСАНИЕ

Объявление и реализация находятся в файле aseqlist.h. Имя DataType используется для представления общего типа данных. Перед включением класса из файла используйте typedef для связывания имени DataType с конкретным типом. Переменная size поддерживает текущий размер списка. Первоначально размер установлен на 0. Так как статический массив используется для реализации списка, константа MaxListSize является верхней границей размера списка. Попытка вставить больше, чем MaxListSize элементов в список приводит к сообщению об ошибке и завершению программы.

Реализация класса SeqList

Данная реализация класса SeqList использует массив listitem для сохранения данных. Коллекция распределяет память для MaxListSize числа элементов типа DataType. Количество элементов в списке содержится в size (член класса). Файлы iostream.h и stdlib.h включены для обеспечения выдачи сообщения об ошибках и для завершения программы, если Insert приведет к тому, что размер превысит MaxListSize.

Закрытый член size содержит длину списка для операций Insert и Delete. Значение size является центральным для конструктора и методов ListSize, ListEmpty и ClearList. Мы включаем конструктор, устанавливающий размер на 0.

// конструктор, устанавливает size в 0

SeqList::SeqList (void) : size(0)

{}

Методы модификации списка

М

етод Insert добавляет новый элемент в конец списка и увеличивает длину на 1. Если при этом превышается размер массива listitem, то метод выводит сообщение об ошибке и завершает программу. Ограничение на размер списка снимается в дальнейшем, где класс реализуется с использованием связанного списка.

Элемент параметра передается в качестве ссылки константе. Если размер DataType большой, использование ссылочного параметра позволяет избежать неэффективного копирования данных, которое необходимо в вызове параметра по значению. Ключевое слово const указывает на то, что фактический параметр не может быть изменен. Этот же тип передачи параметра используется методом Delete.

Insert

// вставить элемент в хвост списка, прервать выполнение программы, если размер списка

// превысит MaxListSize

void SeqList::Insert(const Datatypes item)

{

// проверка размера списка

if (size+1 > MaxListSize)

{

cerr <<"Превышен максимальный размер списка" << endl;

exit(1);

}

// индекс хвоста равен размеру текущего списка

listitem[size] = item;

size++;

}

М

етод Delete определяет первое появление в списке заданного элемента. Функция требует, чтобы был определен оператор сравнения (==) для DataType. В некоторых случаях для этого может потребоваться, чтобы пользователь предоставил особую функцию, которая переопределяет оператор == для конкретного DataType. Если элемент не обнаруживается при индексе i, операция спокойно заканчивается без изменения списка. Если элемент найден, он удаляется из списка перемещением всех элементов с индексами i+1 к концу списка влево на одну позицию.

Например, удаление элемента со значением 45 из списка приведет к смещению влево хвостовых элементов 23 и 8. Длина списка изменяется с 6 на 5. Удаление элемента со значением 30 оставляет список неизменным.

Delete

// поиск и удаление элемента item из списка

void Seqliist :: Delete (const Datatypes item)

{

int i = 0;

// поиск элемента

while (i < size && !(item == listitem[i]))

i++;

if (i < size)

{

// передвинуть хвост списка на одну позицию влево

while (i < size-1)

{

listitem[i] = listitem[i+1];

i++;

}

size--; // уменьшение size на 1

}

}

Методы доступа к списку. Метод GetData возвращает значение данных позицию pos в списке. Если pos не находится в диапазоне от 0 до size-l печатается сообщение об ошибке, и программа завершается.

// возвращает значение элемента списка для индекса pos. если pos не находится в

// диапазоне индексов списка, программа заканчивается с сообщением об ошибке

DataType SeqList::GetData(int pos) const

{

// прервать программу, если pos вне диапазона индексов списка

if (pos < 0 || pos >= size)

{

cerr << "pos выходит за диапазон индексов!" << endl;

exit(1);

}

return listitem[pos];

}

Метод доступа Find принимает параметр, который служит в качестве ключа, и последовательно просматривает список для нахождения совпадения. Если список пустой или совпадение не найдено. Find возвращает 0 (False). Если элемент обнаруживается в списке в позиции с индексом i. Find присваивает запись из listitem[i] соответствующему элементу списка и возвращает 1 (True). Для данных, совпадающих с ключом, процесс присваивания значения данных элемента списка параметру является важнейшим в приложениях, касающихся записей данных. Например, предположим, что DataType – это структура с полем ключа и полем значения, и что оператор == тестирует только поле ключа. При вводе элемент параметра может определять только воле ключа. При выводе элемент присваивается обоим полям.

Find

// использовать item в качестве ключа для поиска в списке, возвращать True, если элемент item

// находится в списке, и False - в противном случае, если элемент найден, присвоить его значение

// параметру item, передаваемому по ссылке

int SeqList::Find(DataType& item) const

{

int i = 0;

if (ListEmpty())

return 0; // возвратить False, если список пуст

while (i < size && ! (item == listitem[i]))

i++;

if (i < size)

{

item = listitem[i]; // присвоить item значение элемента списка

return 1; // возвратить True

}

else

return 0; // возвратить False

}

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

Пример 4.3

Запись Inventoryltem содержит номер детали и количество деталей в запасе. Оператор – сравнивает две записи Inventoryltem, сравнивая поля partNumber. Выполняется поиск SeqList-объекта L для нахождения записи с partNumber 5. Если объект найден, запись обновляется увеличением поля count.

struct Inventoryltem

{

int partNumber;

int count;

}

int operator== (Invntoryltem x, Invntoryltem y)

{

return x.partMumber == y.partHumber;

}

typedef Inventoryltem DataType;

#include "aseqlist.h"

SeqList L;

Inventoryltem item;

item.partNuinber = 5;

if(L.Find(item))

{

L.Delete(item);

item.count++;

L.Insert(item);

}

Так как любой элемент всегда вставляется в хвост списка, порядок (время выполнения) метода Insert зависит от n и равен O(1). Find выполняет последовательный поиск, поэтому среднее время его работы будет 0(п). На протяжение многих проб метод Delete должен проверить в среднем п/2 элементов списка и должен перемещать в среднем п/2 элементов. Это означает, что среднее время выполнения для Delete составляет 0(п). Порядок наихудшего случая для обоих методов Find и Delete также составляет 0(п).

Применение. Объекты SeqList используются для ведения списка имеющихся фильмов, и списка фильмов, взятых для просмотра клиентами в видеомагазине. Каждый элемент в списке является записью, которая состоит из названия фильма и (для проката) имени клиента.

// структура записи для хранения данных о фильме и клиенте

struct FilmData

{

char filmName[32];

char customerName[32] ;

}

Так как метод Find в классе SeqList требует определения оператора сравнения ==, мы перегрузим этот оператор для структуры FilmData. Этот оператор проверяет имя файла, используя функцию C++ strcmp.

// перегрузка ==

int operator == (const FilaData &A, const FilmData *B)

{

return strcmp (A. filmName, B.filniName) ;

}

Чтобы использовать FilmData с классом SeqList, включите объявление

typedef FilmData DataType;

Определение оператора == для FilmData и DataType находятся в файле video.h.

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

Программа 4.2. Видеомагазин

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

#include <iostreain.h>

#include <fstream.h>

#include <stdlib.h>

#include <atring.h>

#include "video.h" // объявления видео-данных

#include "aseqlist.h" // включить класс SeqList

// читать таблицу фильмов с диска

void SetupInventoryList(seqList sinventoryList)

{

if stream filmFile;

FilmData fd;

// открыть файл, с проверкой ошибок

filmFile.open("Films", ios :: in I ios :: nocreate);

if (! filmFile)

{

cerr << "Файл 'films' не найден!" << endl;

exit(1);

}

// читать строки до конца файла;

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

while(filmFile.getline(fd.filmName, 32, ' \n'))

inventoryList.Insert(fd);

}

// печать наименований фильмов

void PrintInvemtoryList(const SeqList SinventoryList)

{

int i;

FilmData fd;

for (i = 0; i < inventoryList.ListSize(); i++)

{

fd = inventoryList.GetData(i);

cout << fd.filmName <<endl;

}

}

// цикл по списку клиентов, печать Клиентов и фильмов

void PrintCustomerList(const SeqList scustomerList)

{

int i

FilmData fd;

for (i = 0; i < customerList.ListSize (); i++)

{

fd=customerList.GetData(i);

cout << fd.customerName << "(" << fd.filmName << ") " << endl;

}

}

void main (void)

{//

SeqList invcmtoryList, customerList;

int i;

FilmData fdata;

char customer [20];

SetuplnventoryList (inventorybist); // читать файл с фильмами

// запрос имени клиента и названия фильма. Если запрошенный фильм имеется в

// наличии, он вносится в список клиентов и удаляется из списка фильмов; в противном

// случае выдается сообщение об отсутствии фильма

for (i=0; i < 4; i++)

{

// ввод имени клиента и названия фильма

cout << "Имя клиента: ";

cin.getline(customer,32,'\n');

cout << "Запрашиваемый фильм: " ;

cin.getline (fdata. filmName, 32, ' \n') ;

if (inventoryList.Find(fdata))

{

strcpy(fdata.customerName, customer) ;

// вставить название фильма в список клиентов

customerList.Insert(fdata);

// удалить из списка фильмов inventoryList.Delete(fdata) ;

}

else

cout << "Сожалею! "<< fdata. filmName << " отсутствует." << endl;

}

cout << endl;

// печать списков клиентов и фильмов

cout << "Клиенты, взявшие фильмы для просмотра" << endl;

PrintCustomerList(customerList) ;

cout « endl;

cout « "Фильмы, оставшиеся в ведомости: endl;

PrintInventoryList (inventoryList);

}

/* <Входной файл "Films ">

Война миров

Касабланка

Грязный Гарри

Дом животных

Десять заповедей

Красавица и зверь

Список Шиндлера

Звуки музыки

La Strata

Звездные войны

<Выполнение программы 4.2>

Имя клиента: Дон Бекер

Запрашиваемый фильм: Дом животных

Имя клиента: Тери Молтон

Запрашиваемый фильм: Красавица и зверь

Имя клиента: Деррик Лопез

Запрашиваемый фильм: La Strata

Имя клиента: Хиллари Дэн

Запрашиваемый фильм: Дом животных

Сожалею! Дом животных отсутствует.

Клиенты, взявшие фильмы для просмотра

Дон Бекер (Дом животных)

Тери Молтон (Красавица и зверь)

Деррик Лопез (La Strata)

Фильмы, оставшиеся в ведомости:

Война миров

Касабланка

Грязный Гарри

Десять заповедей

Список Шиндлера

Звуки музыки

Звездные войны */