Регистрация постояльцев в гостинице

  • Вид работы:
    Курсовая работа (т)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    1,45 Мб
  • Опубликовано:
    2015-03-07
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Регистрация постояльцев в гостинице

САНКТ - ПЕТЕРБУРГСКИЙ УНИВЕРСИТЕТ АЭРОКОСМИЧЕСКОГО ПРИБОРОСТРОЕНИЯ









Отчет по курсовой работе

По предмету Структуры и алгоритмы обработки данных

На тему:

Регистрация постояльцев в гостинице

Выполнил студент 4836 группы:

Колпачков Михаил

Проверил: Матьяш В.А.





Санкт - Петербург 2010 г.

Содержание

Введение

1. Алгоритмы и структуры данных

1.1 Хеш-таблицы. Открытое Хеширование

1.2 АВЛ-дерево

1.3 Обходы бинарных деревьев

1.4 Сортировка пузырьком

1.5 Алгоритм Боуера и Мура (БМ)

1.6 Линейный однонаправленный список

4. Описание программы

5. Тестирование программы

Заключение

Список использованной литературы

Задание на курсовой проект

 

Задачей курсового проекта является разработка информационной системы для заданной предметной области с использованием заданных структур данных и алгоритмов.

предметная область - Регистрация постояльцев в гостинице;

метод хеширования - Открытое хеширование;

метод сортировки - Пузырьковый;

вид списка - Линейный однонаправленный;

метод обхода дерева - Прямой;

алгоритм поиска слова в тексте - Боуера и Мура (БМ).

1       Информационная система для предметной области "Регистрация постояльцев в гостинице" должна осуществлять ввод, хранение, обработку и вывод данных о:

постояльцах;

гостиничных номерах;

вселении и выселении постояльцев.

2       Данные о каждом постояльце должны содержать:

№ паспорта - строка формата "NNNN-NNNNNN", где N - цифры;

ФИО - строка;

Год рождения - целое;

Адрес - строка;

Цель прибытия - строка.

Примечание - длина строк (кроме № паспорта) определяется студентом самостоятельно.

3       Данные о постояльцах должны быть организованны в виде хеш-таблицы, первичным ключом которой является "№ паспорта" Метод хеширования определяется вариантом задания.

4       Данные о каждом гостиничном номере должны содержать:

программа алгоритм сортировка хеширование

№ гостиничного номера - строка формата "ANNN", где A - буква, обозначающая тип номера (Л - люкс, П - полулюкс, О - одноместный, М - многоместный), NNN - порядковый номер (цифры);

Количество мест - целое;

Количество комнат - целое;

Наличие санузла - логическое;

Оборудование - строка.

Примечание - длина строки "Оборудование", содержащая перечень оборудования номера (телевизор, холодильник и пр.) определяется студентом самостоятельно.

5       Данные о гостиничных номерах должны быть организованны в виде АВЛ-дерева поиска, упорядоченного по "№ гостиничного номера".

6       Данные о вселении или выселении постояльцев должны содержать:

№ паспорта - строка, формат которой соответствует аналогичной строке в данных о постояльцах;

№ гостиничного номера - строка, формат которой соответствует аналогичной строке в данных о гостиничных номерах;

Дата заселения - строка;

Дата выселения - строка.

Примечания:

А) Наличие в этих данных записи, содержащей в поле "№ паспорта" значение X и в поле "№ гостиничного номера" значение Y означает заселение постояльца с номером паспорта X в гостиничный номер Y. Отсутствие такой записи означает, что постоялец с номером паспорта X не проживает в гостиничном номере Y.

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

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

8       Информационная система "Регистрация постояльцев в гостинице" должна осуществлять следующие операции:

регистрация нового постояльца;

удаление данных о постояльце;

просмотр всех зарегистрированных постояльцев;

очистка данных о постояльцах;

поиск постояльца по № паспорта. Результаты поиска - все сведения о найденном постояльце и № гостиничного номера, в котором он проживает;

поиск постояльца по ФИО. Результаты поиска - список найденных постояльцев с указанием № паспорта и ФИО;

добавление нового гостиничного номера;

удаление сведений о гостиничном номере;

просмотр всех имеющихся гостиничных номеров;

очистка данных о гостиничных номерах;

поиск гостиничного номера по "№ гостиничного номера". Результаты поиска - все сведения о найденном гостиничном номере, а также ФИО и № паспортов постояльцев, которые вселены в этот гостиничный номер;

поиск гостиничного номера по фрагментам "Оборудования". Результаты поиска - список найденных гостиничных номеров с указанием "№ гостиничного номера, количества мест, количества комнат, оборудования;

регистрация вселения постояльца;

регистрация выселения постояльца.

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

10     Метод поиска постояльца по ФИО определяется студентом самостоятельно. Выбранный метод необходимо сравнить с альтернативными методами.

         Поиск гостиничного номера по фрагментам "Оборудования" должен осуществляться путем систематического обхода АВЛ-дерева поиска. Метод обхода определяется вариантом задания. При поиске гостиничного номера по фрагментам "Оборудования" могут быть заданы как полный перечень оборудования гостиничного номера, так и его часть (например, указан только телевизор). Для обнаружения заданного фрагмента в полном перечне оборудования гостиничного номера должен применяться алгоритм поиска слова в тексте, указанный в варианте задания.

         Регистрация вселения постояльца должна осуществляться только при наличии свободных мест в занимаемом гостиничном номере.


Введение


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

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

ввод, хранение, обработку и вывод данных о постояльцах;

ввод, хранение, обработку и вывод данных о гостиничных номерах;

ввод, хранение, обработку и вывод данных о вселении и выселении постояльцев.

1. Алгоритмы и структуры данных


1.1 Хеш-таблицы. Открытое Хеширование


Хеш-таблица - это обычный массив с необычной адресацией, задаваемой хеш-функцией.

Например, на hashTable рис. 1 - это массив из 8 элементов. Каждый элемент представляет собой указатель на линейный список, хранящий числа. Хеш-функция в этом примере просто делит ключ на 8 и использует остаток как индекс в таблице. Это дает нам числа от 0 до 7. Поскольку для адресации в hashTable нам и нужны числа от 0 до 7, алгоритм гарантирует допустимые значения индексов.

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

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

Рис. 1: Хеш-таблица

Чтобы вставить в таблицу новый элемент, мы хешируем ключ, чтобы определить список, в который его нужно добавить, затем вставляем элемент в начало этого списка. Например, чтобы добавить 11, мы делим 11 на 8 и получаем остаток 3. Таким образом, 11 следует разместить в списке, на начало которого указывает hashTable [3]. Чтобы найти число, мы его хешируем и проходим по соответствующему списку. Чтобы удалить число, мы находим его и удаляем элемент списка, его содержащий.

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

Если хеш-функция распределяет совокупность возможных ключей равномерно по множеству индексов, то хеширование эффективно разбивает множество ключей. Наихудший случай - когда все ключи хешируются в один индекс. При этом мы работаем с одним линейным списком, который и вынуждены последовательно сканировать каждый раз, когда что-нибудь делаем. Отсюда видно, как важна хорошая хеш-функция. Здесь мы рассмотрим лишь несколько из возможных подходов. При иллюстрации методов предполагается, что unsigned char располагается в 8 битах, unsigned short int - в 16, unsigned long int - в 32.

II.      Деление (размер таблицы hashTableSize - простое число). Этот метод использован в последнем примере. Хеширующее значение hashValue, изменяющееся от 0 до (hashTableSize - 1), равно остатку от деления ключа на размер хеш-таблицы. Вот как это может выглядеть:

int HashIndexType;Hash (int Key) {Key % HashTableSize;

}

Для успеха этого метода очень важен выбор подходящего значения hashTableSize. Если, например, hashTableSize равняется двум, то для четных ключей хеш-значения будут четными, для нечетных - нечетными. Ясно, что это нежелательно - ведь если все ключи окажутся четными, они попадут в один элемент таблицы. Аналогично, если все ключи окажутся четными, то hashTableSize, равное степени двух, попросту возьмет часть битов Key в качестве индекса. Чтобы получить более случайное распределение ключей, в качестве hashTableSize нужно брать простое число, не слишком близкое к степени двух.

III.     Мультипликативный метод (размер таблицы hashTableSize есть степень 2n). Значение key умножается на константу, затем от результата берется n бит. В качестве такой константы Кнут рекомендует золотое сечение (sqrt (5) - 1) /2 = 0.6180339887499. Пусть, например, мы работаем с таблицей из hashTableSize=32 (25) элементов, хэширование производится байтами (8 бит, unsigned char). Тогда необходимый множитель: 28*sqrt (5) - 1) /2 = 158.

IV.    Далее, умножая 8-битовый ключ на 158, будем получать некоторое 16-битное число. Для таблицы длиной 25 в качестве хеширующего значения берем 5 старших битов младшего слова, содержащего такое произведение. Вот как можно реализовать этот метод:

/* 8-bit index */unsigned char HashIndexType;const HashIndexType K = 158;

/* 16-bit index */

typedef unsigned short int HashIndexType;const HashIndexType K = 40503;

/* 32-bit index */unsigned long int HashIndexType;const HashIndexType K = 2654435769;

/* w=bitwidth (HashIndexType), size of table=2**m */const int S = w - m;

/* Преобразование типа убирает старшее слово, т. е

* лишние биты слева, а сдвиг убирает лишнее справа

*/HashValue = (HashIndexType) (K * Key) >> S;

Пусть, например, размер таблицы hashTableSize равен 1024 (210). Тогда нам достаточен 16-битный индекс и величине сдвига S будет присвоено значение 16 - 10 = 6. В итоге получаем:

typedef unsigned short int HashIndexType;Hash (int Key) {const HashIndexType K = 40503;const int S = 6;(HashIndexType) (K * Key) >> S;

}

V.      Аддитивный метод для строк переменной длины (размер таблицы равен 256). Для строк переменной длины вполне разумные результаты дает сложение по модулю 256. В этом случае результат hashValue заключен между 0 и 255.

unsigned char HashIndexType;Hash (char *str) {h = 0;(*str) h += *str++;h;

}

VI.     Исключающее ИЛИ для строк переменной длины (размер таблицы равен 256). Этот метод аналогичен аддитивному, но успешно различает схожие слова и анаграммы (аддитивный метод даст одно значение для XY и YX).

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

typedef unsigned char HashIndexType;char Rand8 [256];Hash (char *str) {char h = 0;(*str) h = Rand8 [h ^ *str++];

return h;

}

Здесь Rand8 - таблица из 256 восьмибитовых случайных чисел. Их точный порядок не критичен. Корни этого метода лежат в криптографии; он оказался вполне эффективным.

VIII.  Исключающее ИЛИ для строк переменной длины (размер таблицы <= 65536). Если мы хешируем строку дважды, мы получим хеш-значение для таблицы любой длины до 65536. Когда строка хешируется во второй раз, к первому символу прибавляется 1. Получаемые два 8-битовых числа объединяются в одно 16-битовое.

typedef unsigned short int HashIndexType;char Rand8 [256];Hash (char *str) {h;char h1, h2;(*str == 0) return 0;= *str; h2 = *str + 1;++;(*str) {= Rand8 [h1 ^ *str];= Rand8 [h2 ^ *str];++;

}

/* h is in range 0.65535 */= ( (HashIndexType) h1 << 8) | (HashIndexType) h2;

/* use division method to scale */

return h % HashTableSize

}

Размер хеш-таблицы должен быть достаточно большим, чтобы в ней оставалось разумно большое число пустых мест. Чем меньше таблица, тем больше среднее время поиска ключа в ней. Хеш-таблицу можно рассматривать как совокупность связанных списков. По мере того, как таблица растет, увеличивается количество списков и, соответственно, среднее число узлов в каждом списке уменьшается. Пусть количество элементов равно n. Если размер таблицы равен 1, то таблица вырождается в один список длины n. Если размер таблицы равен 2 и хеширование идеально, то нам придется иметь дело с двумя списками по n/100 элементов в каждом. Это сильно уменьшает длину списка, в котором нужно искать.

1.2 АВЛ-дерево


АВЛ-дерево - сбалансированное по высоте двоичное дерево поиска: для каждой его вершины высота её двух поддеревьев различается не более чем на 1.

АВЛ-деревья названы по первым буквам фамилий их изобретателей, Г.М. Адельсона-Вельского и Е.М. Ландиса, которые впервые предложили использовать АВЛ-деревья в 1962.

Общие свойства

В АВЛ-дереве высоты h имеется не меньше Fh узлов, где Fh - число Фибоначчи. Поскольку

,

где  - золотое сечение,

то имеем оценку на высоту АВЛ-дерева h = O (log (n)), где n - число узлов. Следует помнить, что O (log (n)) - мажоранта, и ее можно использовать только для оценки (Например, если в дереве только два узла, значит в дереве два уровня, хотя log (2) = 1). Для точной оценки глубины дерева следует использовать пользовательскую подпрограмму.

TreeDepth (Tree: TAVLTree): byte;

beginTree <> nil then: = 1 + Max (TreeDepth (Tree^. left),TreeDepth (Tree^. right))

else: = 0;;

Тип дерева можно описать так

TKey = LongInt;= LongInt;= - 1.1;= ^ TAVLNode;= record, right: TAVLTree;

key: TKey;: TInfo;

{ Поле определяющее сбалансированность вершины }: TBalance;;

 

Балансировка

Относительно АВЛ-дерева балансировкой вершины называется операция, которая в случае разницы высот левого и правого поддеревьев = 2, изменяет связи предок-потомок в поддереве данной вершины так, что разница становится <= 1, иначе ничего не меняет. Указанный результат получается вращениями поддерева данной вершины.

Используются 4 типа вращений:

. Малое левое вращение


Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота С <= высота R.

. Большое левое вращение


Данное вращение используется тогда, когда (высота b-поддерева - высота L) = 2 и высота c-поддерева > высота R.

. Малое правое вращение


Данное вращение используется тогда, когда (высота b-поддерева - высота R) = 2 и высота С <= высота L.

. Большое правое вращение


Данное вращение используется тогда, когда (высота b-поддерева - высота R) = 2 и высота c-поддерева > высота L.

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

Из-за условия балансированности высота дерева О (lg (N)), где N - количество вершин, поэтому добавление элемента требует O (lg (N)) операций.

Алгоритм добавления вершины

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

.        Прохода по пути поиска, пока не убедимся, что ключа в дереве нет.

2.      Включения новой вершины в дерево и определения результирующих показателей балансировки.

.        "Отступления" назад по пути поиска и проверки в каждой вершине показателя сбалансированности. Если необходимо - балансировка.

Расширим список параметров обычной процедуры вставки параметром-переменной flag, означающим, что высота дерева увеличилась. Предположим, что процесс из левой ветви возвращается к родителю (рекурсия идет назад), тогда возможны три случая: { hl - высота левого поддерева, hr - высота правого поддерева } Включение вершины в левое поддерево приведет к

.        hl < hr: выравняется hl = hr. Ничего делать не нужно.

2.      hl = hr: теперь левое поддерево будет больше на единицу, но балансировка пока не требуется.

.        hl > hr: теперь hl - hr = 2, - требуется балансировка.

В третьей ситуации требуется определить балансировку левого поддерева. Если левое поддерево этой вершины (Tree^. left^. left) выше правого (Tree^. left^. right), то требуется двойной правый поворот, иначе хватит и малого. Аналогичные (симметричные) рассуждения можно привести и для включение в правое поддерево. Процедура вставки предложенная Н. Виртом

TAVL. InsertNode (Var Tree: TAVLTree; const akey: TKey; const ainfo: TInfo; Var flag: Boolean);

Var, Node2: TAVLTree;Tree = nil then(Tree);: = true;Tree^ do: = akey;: = ainfo;: = nil;: = nil;: = 0;;(AVL. FNodes);if Tree^. key > akey then(Tree^. left,akey,ainfo,flag);flag thenTree^. balance of

: begin Tree^. balance: = 0; flag: = false; end;

: Tree^. balance: = - 1;

: { Balance }: = Tree^. left;Node1^. balance = - 1 then

{ LL }^. left: = Node1^. right;^. right: = Tree;^. balance: = 0;: = Node1;

{LR}: = Node1^. right;^. right: = Node2^. left;^. left: = Node1;^. left: = Node2^. right;^. right: = Tree;Node2^. balance = - 1 then Tree^. balance: = 1 else Tree^. balance: = 0;Node2^. balance = 1 then Node1^. balance: = - 1 else Node1^. balance: = 0;: = Node2;;^. balance: = 0;: = falseif Tree^. key < akey then(Tree^. right,akey,ainfo,flag);flag thenTree^. balance of

: begin Tree^. balance: = 0; flag: = false; end;

: Tree^. balance: = 1;

: { Balance }: = Tree^. right;Node1^. balance = 1 then

{ RR }^. right: = Node1^. left;^. left: = Tree;^. balance: = 0;: = Node1;

{RL}: = Node1^. left;^. left: = Node2^. right;^. right: = Node1;^. right: = Node2^. left;^. left: = Tree;Node2^. balance = 1 then Tree^. balance: = - 1 else Tree^. balance: = 0;Node2^. balance = - 1 then Node1^. balance: = 1 else Node1^. balance: = 0;: = Node2;;^. balance: = 0;: = false;

 

Алгоритм удаления вершины

Для простоты опишем рекурсивный алгоритм удаления. Если вершина - лист, то удалим её и вызовем балансировку всех её предков в порядке от родителя к корню. Иначе найдём самую близкую по значению вершину в поддереве наибольшей высоты (правом или левом) и переместим её на место удаляемой вершины, при этом вызвав процедуру её удаления.

Докажем, что данный алгоритм сохраняет балансировку. Для этого докажем по индукции по высоте дерева, что после удаления некоторой вершины из дерева и последующей балансировки высота дерева уменьшается не более, чем на 1. База индукции: Для листа очевидно верно. Шаг индукции: Либо условие балансированности в корне (после удаления корень может изменится) не нарушилось, тогда высота данного дерева не изменилась, либо уменьшилось строго меньшее из поддеревьев => высота до балансировки не изменилась => после уменьшится не более чем на 1.

Очевидно, в результате указанных действий процедура удаления вызывается не более 3 раз, так как у вершины, удаляемой по 2-му вызову, нет одного из поддеревьев. Но поиск ближайшего каждый раз требует O (N) операций, отсюда видна очевидная оптимизация: поиск ближайшей вершины производится по краю поддерева. Отсюда количество действий O (log (N)).

Расстановка балансов при удалении

Как уже говорилось, если удаляемая вершина - лист, то она удаляется, и обратный обход дерева происходит от родителя удалённого листа. Если не лист - ей находится "замена", и обратный обход дерева происходит от родителя "замены". Непосредственно после удаления элемента - "замена" получает баланс удаляемого узла.

При обратном обходе: если при переходе к родителю пришли слева - баланс уменьшается на 1, если же пришли справа - увеличивается на 1.

Это делается до тех пор, пока при изменении баланса он не станет равным −1 или 1 (обратите внимание на различие с вставкой элемента!): в данном случае такое изменение баланса будет гласить о неизменной дельта-высоте поддеревьев. Повороты происходят по тем же правилам, что и при вставке.

Расстановка балансов при одинарном повороте

Обозначим:

"Current" - узел, баланс которого равен −2 или 2: то есть тот, который нужно повернуть (на схеме - элемент a)

"Pivot" - ось вращения. +2: левый сын Current’а, −2: правый сын Current’а (на схеме - элемент b)

Если поворот осуществляется при вставке элемента, то баланс Pivot’а равен либо 1, либо −1. В таком случае после поворота балансы обоих устанавливаются равными 0.

При удалении всё иначе: баланс Pivot’а может стать равным 0 (в этом легко убедиться).

Приведём сводную таблицу зависимости финальных балансов от направления поворота и исходного баланса узла Pivot:

Направление поворота

Old Pivot. Balance

New Current. Balance

New Pivot. Balance

Левый или Правый

-1 или +1

0

0

Правый

0

-1

+1

Левый

0

+1

-1

 

Расстановка балансов при двойном повороте

Pivot и Current - те же самые, но добавляется третий участник поворота. Обозначим его за "Bottom": это (при двойном правом повороте) левый сын Pivot’а, а при двойном левом - правый сын Pivot’а.

При данном повороте - Bottom в результате всегда приобретает баланс 0, но от его исходного баланса зависит расстановка балансов для Pivot и Current.

Приведём сводную таблицу зависимости финальных балансов от направления поворота и исходного баланса узла Bottom:

Направление

Old Bottom. Balance

New Current. Balance

New Pivot. Balance

Левый или Правый

0

0

0

Правый

+1

0

-1

Правый

-1

+1

0

Левый

+1

-1

0

Левый

-1

0

+1

 

Оценка эффективности

Г.М. Адельсон-Вельский и Е.М. Ландис доказали теорему, согласно которой высота АВЛ-дерева с N внутренними вершинами заключена между log2 (N+1) и 1.4404*log2 (N+2) - 0.328, то есть высота АВЛ-дерева никогда не превысит высоту идеально сбалансированного дерева более, чем на 45%. Для больших N имеет место оценка 1.04*log2 (N). Таким образом, выполнение основных операций 1 - 3 требует порядка log2 (N) сравнений. Экспериментально выяснено, что одна балансировка приходится на каждые два включения и на каждые пять исключений.

1.3 Обходы бинарных деревьев


Существует достаточно много алгоритмов работы с древовидными структурами, в которых часто встречается понятие обхода (traversing) дерева или "прохода" по дереву. При таком методе исследования дерева каждый узел посещается только один раз, а полный обход задает линейное упорядочивание узлов, что позволяет упростить алгоритм, так как при этом можно использовать понятие "следующий" узел. т.е. узел стоящий после данного при выбранном порядке обхода.

Существует несколько принципиально разных способов обхода дерева:

Обход в прямом порядке

Каждый узел посещается до того, как посещены его потомки.

Для корня дерева рекурсивно вызывается следующая процедура:

Посетить узел

Обойти левое поддерево

Обойти правое поддерево

Примеры использования обхода:

·              решение задачи методом деления на части

·              разузлование сверху

·              стратегия "разделяй и властвуй" (Сортировка Фон Hеймана, быстрая сортировка, одновременное нахождение максимума и минимума последовательности чисел, умножение длинных чисел).

1.4 Сортировка пузырьком


Расположим массив сверху вниз, от нулевого элемента - к последнему.

Идея метода: шаг сортировки состоит в проходе снизу вверх по массиву. По пути просматриваются пары соседних элементов. Если элементы некоторой пары находятся в неправильном порядке, то меняем их местами.


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

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

<class T>

void bubbleSort (T a [], long size) {i, j;x;(i=0; i < size; i++) { // i - номер прохода

for (j = size-1; j > i; j--) { // внутренний цикл прохода

if (a [j-1] > a [j]) {=a [j-1]; a [j-1] =a [j]; a [j] =x;

}

}

}

}

Среднее число сравнений и обменов имеют квадратичный порядок роста: Theta (n2), отсюда можно заключить, что алгоритм пузырька очень медленен и малоэффективен.

Тем не менее, у него есть громадный плюс: он прост и его можно по-всякому улучшать.

1.5 Алгоритм Боуера и Мура (БМ)


Алгоритм Бойера-Мура, разработанный двумя учеными - Бойером (Robert S. Boyer) и Муром (J. Strother Moore), считается наиболее быстрым среди алгоритмов общего назначения, предназначенных для поиска подстроки в строке. Прежде чем рассмотреть работу этого алгоритма, уточним некоторые термины. Под строкой мы будем понимать всю последовательность символов текста. Собственно говоря, речь не обязательно должна идти именно о тексте. В общем случае строка - это любая последовательность байтов. Поиск подстроки в строке осуществляется по заданному образцу, т.е. некоторой последовательности байтов, длина которой не превышает длину строки. Наша задача заключается в том, чтобы определить, содержит ли строка заданный образец.

Простейший вариант алгоритма Бойера-Мура состоит из следующих шагов. На первом шаге мы строим таблицу смещений для искомого образца. Процесс построения таблицы будет описан ниже. Далее мы совмещаем начало строки и образца и начинаем проверку с последнего символа образца. Если последний символ образца и соответствующий ему при наложении символ строки не совпадают, образец сдвигается относительно строки на величину, полученную из таблицы смещений, и снова проводится сравнение, начиная с последнего символа образца. Если же символы совпадают, производится сравнение предпоследнего символа образца и т.д. Если все символы образца совпали с наложенными символами строки, значит мы нашли подстроку и поиск окончен. Если же какой-то (не последний) символ образца не совпадает с соответствующим символом строки, мы сдвигаем образец на один символ вправо и снова начинаем проверку с последнего символа. Весь алгоритм выполняется до тех пор, пока либо не будет найдено вхождение искомого образца, либо не будет достигнут конец строки.

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

Величина смещения для каждого символа образца зависит только от порядка символов в образце, поэтому смещения удобно вычислить заранее и хранить в виде одномерного массива, где каждому символу алфавита соответствует смещение относительно последнего символа образца. Поясним все вышесказанное на простом примере. Пусть у нас есть набор символов из пяти символов: a, b, c, d, e и мы хотим найти вхождение образца "abbad" в строке "abeccacbadbabbad”. Следующие схемы иллюстрируют все этапы выполнения алгоритма:

 

Таблица смещений для образца "abbad”.


Начало поиска. Последний символ образца не совпадает с наложенным символом строки. Сдвигаем образец вправо на 5 позиций:


Три символа образца совпали, а четвертый - нет. Сдвигаем образец вправо на одну позицию:


Последний символ снова не совпадает с символом строки. В соответствии с таблицей смещений сдвигаем образец на 2 позиции:


Еще раз сдвигаем образец на 2 позиции:


Теперь, в соответствии с таблицей, сдвигаем образец на одну позицию, и получаем искомое вхождение образца:


1.6 Линейный однонаправленный список


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


Существует два основных способа построения односвязного списка. Первый способ - помещать новые элементы в конец списка. Второй - вставлять элементы в определенные позиции списка, например, в порядке возрастания. От способа построения списка зависит алгоритм функции добавления элемента. Давайте начнем с более простого способа создания списка путем помещения элементов в конец.

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

struct address {name [40];street [40];city [20];state [3];zip [11];

struct address *next; /* ссылка на следующий адрес */

} info;

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

void slstore (struct address *i,address **last)

{(! *last) *last = i; /* первый элемент в списке */(*last) - >next = i;>next = NULL;

*last = i;

}

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

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


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

Следующая функция, sls_store (), вставляет структуры типа address в список рассылки, упорядочивая его по возрастанию значений в поле name. Функция принимает указатели на указатели на первый и последний элементы списка, а также указатель на вставляемый элемент. Поскольку первый или последний элементы списка могут измениться, функция sls_store () при необходимости автоматически обновляет указатели на начало и конец списка. При первом вызове данной функции указатели first и last должны быть равны нулю.

/* Вставка в упорядоченный односвязный список. */sls_store (struct address *i, /* новый элемент */address **start, /* начало списка */address **last) /* конец списка */

{address *old, *p;= *start;

if (! *last) { /* первый элемент в списке */

i->next = NULL;

*last = i;

*start = i;;

}= NULL;(p) {(strcmp (p->name, i->name) <0) {= p;= p->next;

}{(old) { /* вставка в середину */

old->next = i;>next = p;

return;

}>next = p; /* вставка в начало */

*start = i;;

}

}

(*last) - >next = i; /* вставка в конец */>next = NULL;

*last = i;

}

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

void display (struct address *start)

{(start) {("%s\n", start->name);

start = start->next;

}

}

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

Для получения элемента из списка нужно просто пройти по цепочке ссылок. Ниже приведен пример функции поиска по содержимому поля name:

struct address *search (struct address *start, char *n)

{(start) {(! strcmp (n, start->name)) return start;= start->next;

}NULL; /* подходящий элемент не найден */

}

Поскольку функция search () возвращает указатель на элемент списка, содержащий искомое имя, возвращаемый тип описан как указатель на структуру address. При отсутствии в списке подходящих элементов возвращается нуль (NULL).

Удаление элемента из односвязного списка выполняется просто. Так же, как и при вставке, возможны три случая: удаление первого элемента, удаление элемента в середине, удаление последнего элемента. На рис.4 показаны все три операции.


Ниже приведена функция, удаляющая заданный элемент из списка структур address.

void sldelete (address *p, /* предыдущий элемент */address *i, /* удаляемый элемент */address **start, /* начало списка */address **last) /* конец списка */

{(p) p->next = i->next;*start = i->next;(i==*last && p) *last = p;

}

Функции sldelete () необходимо передавать указатели на удаляемый элемент, предшествующий удаляемому, а также на первый и последний элементы. При удалении первого элемента указатель на предшествующий элемент должен быть равен нулю (NULL). Данная функция автоматически обновляет указатели start и last, если один из них ссылается на удаляемый элемент.

У односвязных списков есть один большой недостаток: односвязный список невозможно прочитать в обратном направлении. По этой причине обычно применяются двусвязные списки.

4. Описание программы


Программа состоит из нескольких файлов исходных текстов программ.

Все функции описаны в хедер файлах. Их предназначение:

Parserlib. h - функции-парсеры для проверки валидности данных;

Registration. h - Работа с односвязным списком,

функциональность: регистрация вселения/выселения постояльца

hastable. h - Работа с хеш-таблицей

функциональность: работа с информацией о постояльцах

avl. h - работа с АВЛ-деревом

функциональность: работа с гостиничными номерами

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

Исходные тексты программы:

Avl. h

/**********************************************

работа с АВЛ-деревом

функциональность: работа с гостиничными номерами

***********************************************/

// структура нодаNODE

{Number [5]; // номер гостиничного номераCountBads; // количество спальных местCountRooms; // количество комнатCountFreeBads; // количество оставшихся свободных местtoilet; // наличие санузлаequipment [1000]; // оборудование номераFlag;NODE *Left_Child;NODE *Right_Child;

};convertnum (char *); // вспомогательная функция для получения номера гостиничного номера без префикса в виде intNODE * Binary_Tree (char *, int, int, char, char *, struct NODE *, int *); // Добавление элемента в деревоNODE * Balance_Right_Heavy (struct NODE *, int *); // перебалансировка дерева при удалении узлаNODE * Balance_Left_Heavy (struct NODE *, int *); // перебалансировка дерева при удалении узлаNODE * Delete_Element (struct NODE *, int, int *, int *); // удаление узла из дереваDeleteAllAVL (struct NODE **); // удаление всего дереваOutput (struct NODE *); // вывод дерева на экранboyer_moore (char *, char *); // функция поиска подстроки по алгоритму Боуера-Мураfindequip (struct NODE *, char *, int *); // ищем номера по фрагментам оборудованияfind_hotel_room (struct NODE *, char *, struct element *, struct NodeHash **, int *); // поиск по № гостиничного номера (с использованием дерева, хеш-таблицы и тд)find_numbers (struct NODE *, char *, int *, int *, int); // проверка наличия гостиничного номера в базе (поиск номера в дереве)printsk (char *,.);

avl. cpp

#define _CRT_SECURE_NO_WARNINGS

#include<string. h>

#include<conio. h>

#include<windows. h>

#include "avl. h"

#include "hashtable. h"

#include "registration. h"

# define T 1

// ----------------------------------------

/*вспомогательная функция для получения номера гостиничного номера без префикса в виде int*/convertnum (char *Number)

{tmp [3];[0] =* (Number+1);[1] =* (Number+2);[2] =* (Number+3);atoi (tmp);

}

// ----------------------------------------

/* Добавление элемента в дерево */NODE * Binary_Tree (char *Number, int CountBads, int CountRooms, char toilet, char *equipment, struct NODE *Parent, int *H)

{NODE *Node1;NODE *Node2;

// если дерево пусто(! Parent)

{= (struct NODE *) malloc (sizeof (struct NODE));(Parent->Number,Number);>Number [4] ='\0';>CountBads=CountBads;>CountFreeBads=CountBads;>CountRooms=CountRooms;>toilet=toilet;(Parent->equipment,equipment);>Left_Child = NULL;>Right_Child = NULL;>Flag = 0;

*H = T;(Parent);

}(convertnum (Number) == convertnum (Parent->Number))

{("Невозможно добавление двух номеров с одинаковыми номерами!");

_getch ();(Parent);

}(convertnum (Number) < convertnum (Parent->Number))

{>Left_Child = Binary_Tree (Number,CountBads, CountRooms, toilet,equipment, Parent->Left_Child, H);(*H)

/* Левая ветвь стала выше */(Parent->Flag)

{1:

/* Right heavy */>Flag = 0;

*H = F;;0:

/* Balanced tree */>Flag = - 1;;- 1:

/* Left heavy */= Parent->Left_Child;(Node1->Flag == - 1)

{

// printf ("\n Left to Left Rotation\n");>Left_Child = Node1->Right_Child;->Right_Child = Parent;>Flag = 0;= Node1;

}

{

// printf ("\n Left to right rotation\n");= Node1->Right_Child;->Right_Child = Node2->Left_Child;->Left_Child = Node1;>Left_Child = Node2->Right_Child;->Right_Child = Parent;(Node2->Flag == - 1)>Flag = 1;>Flag = 0;(Node2->Flag == 1)->Flag = - 1;->Flag = 0;= Node2;

}>Flag = 0;

*H = F;

}

}(convertnum (Number) > convertnum (Parent->Number))

{>Right_Child = Binary_Tree (Number, CountBads, CountRooms, toilet,equipment, Parent->Right_Child, H);(*H)

/* Правая ветвь стала выше */(Parent->Flag)

{- 1:

/* Left heavy */>Flag = 0;

*H = F;;0:

/* Balanced tree */>Flag = 1;;1:

/* Right heavy */= Parent->Right_Child;(Node1->Flag == 1)

{

// printf ("\n Right to Right Rotation\n");>Right_Child = Node1->Left_Child;->Left_Child = Parent;>Flag = 0;= Node1;

}

{

// printf ("\n Right to Left Rotation\n");= Node1->Left_Child;->Left_Child = Node2->Right_Child;->Right_Child = Node1;>Right_Child = Node2->Left_Child;->Left_Child = Parent;(Node2->Flag == 1)>Flag = - 1;>Flag = 0;(Node2->Flag == - 1)->Flag = 1;->Flag = 0;= Node2;

}>Flag = 0;

*H = F;

}

}(Parent);

}

// ----------------------------------------

/* Balancing Right Heavy */NODE * Balance_Right_Heavy (struct NODE *Parent, int *H)

{NODE *Node1, *Node2;(Parent->Flag)

{- 1:>Flag = 0;;0:>Flag = 1;

*H = F;;1:

/* Rebalance */= Parent->Right_Child;(Node1->Flag >= 0)

{

// printf ("\n Right to Right Rotation\n");>Right_Child = Node1->Left_Child;->Left_Child = Parent;(Node1->Flag == 0)

{>Flag = 1;->Flag = - 1;

*H = F;

}>Flag = Node1->Flag = 0;= Node1;

}

{

// printf ("\n Right to Left Rotation\n");= Node1->Left_Child;->Left_Child = Node2->Right_Child;->Right_Child = Node1;>Right_Child = Node2->Left_Child;->Left_Child = Parent;(Node2->Flag == 1)>Flag = - 1;>Flag = 0;(Node2->Flag == - 1)->Flag = 1;->Flag = 0;= Node2;->Flag = 0;

}

}(Parent);

}

// ----------------------------------------

/* Balancing Left Heavy */NODE * Balance_Left_Heavy (struct NODE *Parent, int *H)

{NODE *Node1, *Node2;(Parent->Flag)

{1:>Flag = 0;;0:>Flag = - 1;

*H = F;;- 1:

/* Rebalance */= Parent->Left_Child;(Node1->Flag <= 0)

{

// printf ("\n Left to Left Rotation\n");>Left_Child = Node1->Right_Child;->Right_Child = Parent;(Node1->Flag == 0)

{>Flag = - 1;->Flag = 1;

*H = F;

}>Flag = Node1->Flag = 0;= Node1;

}

{

// printf ("\n Left to Right Rotation\n");= Node1->Right_Child;->Right_Child = Node2->Left_Child;->Left_Child = Node1;>Left_Child = Node2->Right_Child;->Right_Child = Parent;(Node2->Flag == - 1)>Flag = 1;>Flag = 0;(Node2->Flag == 1)->Flag = - 1;->Flag = 0;= Node2;->Flag = 0;

}

}(Parent);

}

// ----------------------------------------

/* Перемещаем ноду с найденым ключом с последним правым ключем левого ребенка */NODE * Delete (struct NODE *R, struct NODE *Temp, int *H)

{NODE *Dnode = R;(R->Right_Child! = NULL)

{>Right_Child = Delete (R->Right_Child, Temp, H);(*H)= Balance_Left_Heavy (R, H);

}

{= R;(Temp->Number,R->Number);= R->Left_Child;(Dnode);

*H = T;

}(R);

}

// ----------------------------------------

/* Удаление элемента из дерева */NODE * Delete_Element (struct NODE *Parent, int Number, int *H, int *status)

{NODE *Temp;(! Parent)

{("\n База пуста или номер не найден!");(Parent);

}

{(Number < convertnum (Parent->Number))

{>Left_Child = Delete_Element (Parent->Left_Child, Number, H, status);(*H)= Balance_Right_Heavy (Parent, H);

}if (Number > convertnum (Parent->Number))

{>Right_Child = Delete_Element (Parent->Right_Child, Number, H, status);(*H)= Balance_Left_Heavy (Parent, H);

}

{= Parent;(Temp->Right_Child == NULL)

{= Temp->Left_Child;

*H = T;(Temp);

*status=1;

}if (Temp->Left_Child == NULL)

{= Temp->Right_Child;

*H = T;(Temp);

*status=1;

}

{>Left_Child = Delete (Temp->Left_Child, Temp, H);(*H)= Balance_Right_Heavy (Parent, H);

*status=1;

}

}

}(Parent);

}

// ----------------------------------------

/*удаление всего дерева*/DeleteAllAVL (struct NODE **Parent)

{NODE *t = *Parent;(t! = NULL)

{(&t->Left_Child);(&t->Right_Child);(t);

}

}

// ----------------------------------------

/* Вывод дерева */Output (struct NODE *Tree)

{(Tree)

{(Tree->Left_Child);("\n№ гостиничного номера: %s \nКоличество мест: %i \nКоличество комнат: %i \nНаличие санузла: %c \nОборудование: %s\n\n", Tree->Number, Tree->CountBads, Tree->CountRooms, Tree->toilet,Tree->equipment);(Tree->Right_Child);

}

}

// ----------------------------------------

/*функция поиска подстроки по алгоритму Боуера-Мура*/boyer_moore (char * haystack, char * needle)

{i, j, k;needle_table [256];needle_len = strlen (needle);haystack_len = strlen (haystack);*tmp= (char*) malloc (haystack_len*sizeof (char));(tmp,haystack);(needle_len <= haystack_len)

{(i = 0; i < 256; i++)_table [i] = needle_len;(i = 1; i < needle_len; i++)_table [needle [i]] = needle_len-i;= needle_len;= i;( (j > 0) && (i <= haystack_len))

{= needle_len;= i;( (j > 0) && (tmp [k-1] == needle [j-1]))

{-;-;

}+= needle_table [tmp [i]];

}(k > haystack_len - needle_len)0;k + 1;

}0;

}

// ----------------------------------------

/*поиск № по номеру гостиничного номера*/find_hotel_room (struct NODE *Tree, char *Number, struct element *pbegin, struct NodeHash **hashtable, int *status)

{element *pv=pbegin;hashkey;NodeHash *tmp;(Tree)

{_hotel_room (Tree->Left_Child, Number, pbegin, hashtable, status);_hotel_room (Tree->Right_Child, Number, pbegin, hashtable, status);(strcmp (Tree->Number,Number) ==0)

{

*status=1;("\n№ гостиничного номера: %s \nКоличество мест: %i \nКоличество комнат: %i \nНаличие санузла: %c \nОборудование: %s\n\n", Tree->Number, Tree->CountBads, Tree->CountRooms, Tree->toilet,Tree->equipment);(pv! =0)

{(strcmp (pv->Number,Number) ==0)

{("\nНомер паспорта заселенного постояльца: %s", pv->passport);=hash (pv->passport);=* (hashtable+hashkey);(tmp! =0)

{(strcmp (tmp->passport,pv->passport) ==0)("\nФИО: %s",tmp->fio);=tmp->next;

}

}=pv->next;

}

}

}

}

// ----------------------------------------

/*ищем номера по фрагментам оборудования*/findequip (struct NODE *Tree, char *equip, int *status)

{(Tree)

{(boyer_moore (Tree->equipment, equip))

{("\n№ гостиничного номера: %s \nКоличество мест: %i \nКоличество комнат: %i \nНаличие санузла: %c \nОборудование: %s\n\n", Tree->Number, Tree->CountBads, Tree->CountRooms, Tree->toilet,Tree->equipment);

*status=1;

}(Tree->Left_Child, equip, status);(Tree->Right_Child, equip, status);

}

}

// ----------------------------------------

/*проверка наличия гостиничного номера в базе (поиск номера в дереве)

и вычисляем количество оставшихся свободных мест в номере*/find_numbers (struct NODE *Tree, char *Number, int *status, int *CountFreeBads, int key)

{(Tree)

{(strcmp (Tree->Number,Number) ==0)

{

*status=1;(key==0)

*CountFreeBads=-- (Tree->CountFreeBads);if (Tree->CountFreeBads==-1) *CountFreeBads=++ (++ (Tree->CountFreeBads));;

}_numbers (Tree->Left_Child, Number, status, CountFreeBads, key);_numbers (Tree->Right_Child, Number, status, CountFreeBads, key);

}

}

Hastable. h

/**********************************************

Работа с хеш-таблицей

функциональность: работа с информацией о постояльцах

***********************************************/

// структура элемента хеш-таблицыNodeHash{passport [12];fio [100];birthdayyear;adress [300];target [300];NodeHash *next;

};hash (char *); // вычисление хеш-функцииadd_hash_element (struct NodeHash **, struct NodeHash *, int); // добавление элемента в хеш-таблицуdel_hash_element (struct NodeHash **,char *, int); // удаление элемента из хеш-таблицыview_hash_elements (struct NodeHash **); // просмотр всех элементов хеш-таблицыclear_hash_table (struct NodeHash **); // очистка хеш-таблицыfindfio_hash_table (struct NodeHash **,char *); // поиск в хеш-таблице по ФИОfindpassport_hash_table (struct NodeHash **, char *, struct element *); // поиск в хеш-таблице по паспорту и вывод результатов по таблице и спискуfind_repeats (struct NodeHash **, int, char *); // поиск совпадений номеров паспортов в таблице при попытке добавить нового постояльцаprintsk (char *,.);

hashtable. cpp

#define _CRT_SECURE_NO_WARNINGS

#include<string. h>

#include<stdlib. h>

#include "hashtable. h"

#include "registration. h"

// -----------------------------------------

/*вычисление хеш-функции (метод деления) */hash (char *passport)

{intpas,k=0;tmp [10];(int i=0; i<11; i++)(i==4) continue;

{tmp [k] =* (passport+i);++;

}=atoi (tmp);(intpas % 97);

}

// -----------------------------------------

/* Добавление нового элемента в хеш таблицу. */add_hash_element (struct NodeHash **hashtable, struct NodeHash *pv, int hashkey)

{( (* (hashtable+hashkey)) - >birthdayyear==0) // если по данному ключу элементов нет

{( (* (hashtable+hashkey)) - >adress,pv->adress);

(* (hashtable+hashkey)) - >birthdayyear=pv->birthdayyear;( (* (hashtable+hashkey)) - >fio,pv->fio);( (* (hashtable+hashkey)) - >passport,pv->passport);( (* (hashtable+hashkey)) - >target,pv->target);

(* (hashtable+hashkey)) - >next=0;

}// добавляем в начало списка

{*hashtableitem;= (* (hashtable+hashkey));

(* (hashtable+hashkey)) =pv;>next=hashtableitem;

}

}

// -----------------------------------------

/* Удаление элемента из хеш-таблицы. */del_hash_element (struct NodeHash **hashtable, char *passport, int hashkey)

{level=0;*p,*p1,*p2;= (* (hashtable+hashkey));(p! =0)

{(strcmp (p->passport,passport) ==0) // проверяем совпадение заданного ключа и значения в таблице

{( (level==0) && (p->next==0)) // первый и единственный элемент

{( (* (hashtable+hashkey)) - >adress,"\0");

(* (hashtable+hashkey)) - >birthdayyear=0;( (* (hashtable+hashkey)) - >fio,"\0");

(* (hashtable+hashkey)) - >next=0;( (* (hashtable+hashkey)) - >passport,"0000-000000");( (* (hashtable+hashkey)) - >target,"\0");

}if ( (level! =0) && (p->next==0)) // последний элемент

{(p);->next=0;

}if ( (level==0) && (p->next! =0)) // первый элемент

{=p;=p->next;(p2);

}if ( (level! =0) && (p->next! =0)) // в середине списка

{->next=p->next;(p);

}("\nУдаление завершено. ");;

}=p;=p->next;++;

}("\nПостояльца с таким паспортом не найдено!");

}

// -----------------------------------------

/* Просмотр элементов хеш-таблицы */view_hash_elements (struct NodeHash **hashtable)

{*p;count=0;(int i=0; i<97; i++)

{=* (hashtable+i);(strcmp (p->passport,"0000-000000")! =0)

{("№ паспорта: %s\nФИО: %s\nГод рождения: %i\nАдрес: %s\nЦель прибытия: %s\n\n",p->passport,p->fio,p->birthdayyear,p->adress,p->target);++;(p->next==0) break;=p->next;

}

}(count==0) printsk ("\nДанные отсутсвуют");

}

// -----------------------------------------

/* Очистка хеш-таблицы */clear_hash_table (struct NodeHash **hashtable)

{*p,*p1,*p2;level=0;(int i=0; i<97; i++)

{= (* (hashtable+i));=0;(p->next==0 && p->birthdayyear! =0) // если элемент единственный

{(p->adress,"\0");>birthdayyear=0;(p->fio,"\0");(p->passport,"0000-000000");(p->target,"\0");;

}

{=p->next;(p2! =0)

{(level==0)

{(p->adress,"\0");>birthdayyear=0;(p->fio,"\0");(p->passport,"0000-000000");(p->target,"\0");>next=0;++;

}

{=p2;=p2->next;(p1);++;

}

}

}

}

}

// -----------------------------------------

/* Поиск в хеш-таблице по фио */findfio_hash_table (struct NodeHash **hashtable,char *fio)

{*p;count=0;(int i=0; i<97; i++)

{= (* (hashtable+i));(strcmp (p->passport,"0000-000000")! =0)

{(strcmp (p->fio,fio) ==0)

{("№ паспорта: %s\nФИО: %s\n\n",p->passport,p->fio);++;

}(p->next==0);=p->next;

}

}(count==0) printsk ("Совпадений не найдено. ");

}

// -----------------------------------------

/* поиск в хеш-таблице и списке по паспорту */findpassport_hash_table (struct NodeHash **hashtable, char *passport, struct element *pbegin)

{*p;element *pv=pbegin;count=0;(int i=0; i<97; i++)

{= (* (hashtable+i));(strcmp (p->passport,"0000-000000")! =0)

{(strcmp (p->passport,passport) ==0)

{("№ паспорта: %s\nФИО: %s\nГод рождения: %i\nАдрес: %s\nЦель прибытия: %s\n\n",p->passport,p->fio,p->birthdayyear,p->adress,p->target);++;

}(p->next==0);=p->next;

}

}(count==0)

{("Совпадений не найдено. ");;

}(strcmp ( (pbegin) - >startdate,"00.00.0000") ==0)

{("Нет заселенных зарегистрированных постояльцев. ");;

}(pv! =0)

{(strcmp (pv->passport,passport) ==0)("\nКлиент зарегистрирован в номере: %s", pv->Number);

(pv) = (pv) - >next;

}

}

// -----------------------------------------

/* Поиск совпадений введенного паспорта и паспортов в хеш-таблице */find_repeats (struct NodeHash **hashtable, int hashkey, char *passport)

{NodeHash *tmp=* (hashtable+hashkey);(tmp! =0)

{(strcmp (tmp->passport,passport) ==0)1;=tmp->next;

}0;

}

Parserlib. h

/**********************************************

функции-парсеры для проверки валидности данных

***********************************************/pars_passport (char *); // проверка корректности номера паспортаpars_fio (char *); // проверка корректности ФИОpars_year (int); // проверка возрастаpars_number (char *); // проверка номера гостиничного номераpars_num (char *); // проверка, что все символы лежат в диапазоне 0.9pars_bool (char *); // проверка y/n/yes/nopars_date (char *); // проверка датыprintsk (char *,.);

parserlib. cpp

#include "parserlib. h"

#include <string. h>

// ----------------------------------------

/*проверка корректности номера паспорта*/pars_passport (char *passport)

{(strcmp (passport,"0000-000000") ==0)

{("\nНевозможный номер паспорта!");1;

}( (* (passport+4)! ='-') | (strlen (passport)! =11) | (strlen (passport) ==0))

{("\nНекорректный формат паспорта");1;

}(int i=0; i<11; i++)(i==4) continue;if (! (* (passport+i) >='0' && * (passport+i) <='9'))

{("\nНекорректный формат паспорта");1;

}0;

}

// ----------------------------------------

/*проверка корректности введенного ФИО*/pars_fio (char *fio)

{(strlen (fio) ==0)1;(int i=0; i<100; i++)

{(i==strlen (fio)) break;( ( (* (fio+i) >='A' && * (fio+i) <='z')) || (* (fio+i) ==' ') || (* (fio+i) =='-') || (* (fio+i) =='\''));

{("\nНекорректный ввод ФИО");1;

}

}0;

}

// -----------------------------------------

/* Проверка корректности введенного года рождения */pars_year (int year)

{(year<=1900 || year>=2010)

{("\nНевозможный возраст!");1;

}0;

}

// ----------------------------------------

/*проверка корректности введенного номера гостиничного номера*/pars_number (char *number)

{(strlen (number)! =4)

{("\nНекорректный номер");1;

}(*number! ='l' && *number! ='p' && *number! ='o' && *number! ='m')

{("\nНекорректный тип номера");1;

}(int i=1; i<4; i++)( (* (number+i) >='0') && (* (number+i) <='9'));

{("\nНекорректный номер");1;

}0;

}

// ----------------------------------------

/*проверка корректности введенного числа*/pars_num (char *number)

{(strlen (number) ==0)1;(unsigned int i=0; i<strlen (number); i++)

{( (* (number+i) >='0') && (* (number+i) <='9'));

{("\nНекорректный ввод");1;

}

}0;

}

// ----------------------------------------

/*проверка корректности yes/no/y/n*/pars_bool (char *letter)

{(strlen (letter) ==0)1;(strcmp (letter,"y")! =0 && strcmp (letter,"n")! =0 && strcmp (letter,"yes") && strcmp (letter,"no"))

{("Неверный ввод");1;

}0;

}

// ----------------------------------------

/*проверка корректности введенной даты*/pars_date (char *date)

{countpoints=0;(strlen (date) ==0)1;(strlen (date)! =10)

{("\nНекорректный ввод");1;

}(unsigned int i=0; i<strlen (date); i++)

{( (* (date+i) >='0') && (* (date+i) <='9') || * (date+i) == '. ')

{(* (date+i) =='. ') countpoints++;;

}

{("\nНекорректный ввод");1;

}

}(countpoints>2)

{("\nНекорректный ввод");1;

}(* (date+2)! ='. ' && * (date+5)! ='. ')

{("\nНекорректный ввод");1;

}0;

}

Registration. h

Работа с односвязным списком,

функциональность: регистрация вселения/выселения постояльца

***********************************************/

/*структура элемента списка*/element

{passport [12]; // № паспортаNumber [5]; // № гостиничного номераstartdate [11]; // даты заезда и выезда в формате dd. mm. yyyyenddate [11];element *next;

};element * create (void); // инициализирование ноды спискаadd_registration (struct element **, char *, char *, char *, char *); // добавление элемента в список (вселение постояльца)del_registration (struct NODE *, struct element **, char *, char *); // удаление элемента из списка (выселение постояльца)bubble_sort (struct element **); // сортировка пузырькомcheck_registration (struct element *, char *, char *); // проверка на двойную регистрацию одного постояльца в один номерcheck_reg (struct element *, char *); // проверка на попытку удалить зарегистрированного постояльца из базыfind_number (struct element *, char *); // проверка заселенности номераprintsk (char *,.);

registration. cpp

#define _CRT_SECURE_NO_WARNINGS

#include<string. h>

#include<windows. h>

#include<stdio. h>

#include<conio. h>

#include "registration. h"

#include "avl. h"

// -----------------------------------------

/* Добавление нового элемента в список. */add_registration (struct element **pbegin, char *passport, char *Number, char *startdate, char *enddate)

{element *tmp=*pbegin;

/* Создание первого элемента в списке. */(strcmp ( (*pbegin) - >passport,"ffff-ffffff") == 0)

{( (*pbegin) - >passport,passport);( (*pbegin) - >Number,Number);( (*pbegin) - >startdate,startdate);( (*pbegin) - >enddate,enddate);;

}

// /---------------------------------element *pv = new struct element; // Создаем новый элемент(pv->passport,passport); // заполняем поля информацией(pv->Number,Number);(pv->startdate,startdate);(pv->enddate,enddate);>next = 0;

/* Вставка в конец списка. */(tmp->next! =0)=tmp->next;>next = pv;;

}

// ---------------------------------------

/* Создание пустого списка. */element * create (void)

{element *pv = new struct element;(pv->passport,"ffff-ffffff");(pv->Number,"l000");(pv->startdate,"00.00.0000");(pv->enddate,"00.00.0000");>next = 0;pv;

}

// ---------------------------------------

/*Удаление элемента списка*/del_registration (struct NODE *Tree, struct element **pbegin, char *passport, char *number)

{element *pv=*pbegin,*tmp,*prev=*pbegin;CountFreeBads, status;(pv! =0)

{( (strcmp (pv->passport,passport) ==0) && (strcmp (pv->Number,number) ==0))

{_numbers (Tree, number, &status, &CountFreeBads, 1); // увеличиваем кол-во свободных мест в номере( (*pbegin) - >next==0) // если элемент в списке единственный

{(pv->passport,"ffff-ffffff");(pv->Number,"l000");(pv->startdate,"00.00.0000");(pv->enddate,"00.00.0000");("\nok");

_getch ();;

}if (pv->next==0) // если элемент списка последний

{>next=0;(pv);("\nok");

_getch ();;

}if (pv== (*pbegin)) // удаление первого элемента

{= (*pbegin);

(*pbegin) = (*pbegin) - >next;(tmp);("\nok");;

}// удаление из середины

{=pv;>next=pv->next;(tmp);("\nok");

_getch ();;

}

}=pv;=pv->next;

}("\nДанные введены неправильно или такой записи нет!");

_getch ();

}

// ---------------------------------------

/*Сортировка списка пузырьком*/bubble_sort (struct element **pbegin)

{status=1;replace=0;i=1;element *pv=*pbegin,*tmp,*prev=*pbegin, *next= (*pbegin) - >next;(pv! =0)

{++;=pv->next;

}++;(status==1)

{=*pbegin;= (*pbegin) - >next;=0;-;=1;(i<replace)

{(convertnum (pv->Number) >convertnum (next->Number))

{>next=next->next;>next=pv;=next;=pv;=tmp;=1;

}(i==1)

*pbegin=pv;>next=pv;=pv;=pv->next;=next->next;++;

}

}

}

// ---------------------------------------

/*проверка зарегистрирован ли клиент уже в этом номере*/check_registration (struct element *pbegin, char *passport, char *number)

{element *pv=pbegin;( (pv)! =0)

{(strcmp (pv->passport,passport) ==0 && (strcmp (pv->Number,number) ==0))

{("Нельзя заселить одного и того же постояльца в один номер дважды");1;

}=pv->next;

}0;

}

// ---------------------------------------

/*проверка на попытку удалить зарегистрированного постояльца из базы*/check_reg (struct element *pbegin, char *passport)

{element *pv=pbegin;( (pv)! =0)

{(strcmp (pv->passport,passport) ==0)1;=pv->next;

}0;

}

// ---------------------------------------

/*проверка, есть ли зарегистрированный постоялец по запросу*/find_number (struct element *pbegin, char *number)

{element *pv=pbegin;(pv! =0)

{(convertnum (pv->Number) ==atoi (number))1;=pv->next;

}0;

}

Main. cpp

/**********************************************

КУРСОВАЯ РАБОТА ПО ПРЕДМЕТУ "САОД" 2 КУРС 4 СЕМЕСТР

НА ТЕМУ: "РЕГИСТРАЦИЯ ПОСТОЯЛЬЦЕВ В ГОСТИНИЦЕ"

**********************************************/

# define _CRT_SECURE_NO_WARNINGS

# include<stdio. h>

# include<conio. h>

# include<windows. h>

# include "avl. h"

# include "hashtable. h"

# include "registration. h"

# include "parserlib. h"

// ----------------------------------------

/* Преобразования кодовой таблицы для Windows */printsk (char *format,.)

{buf [1000];_list ptr;(format, buf);_start (ptr, format);(buf, ptr);

}

// ----------------------------------------

/*меню*/menu ()

{("Регистрация постояльцев в гостинице\n");("\nВыберите действие: \n");("1. Регистрация нового постояльца\n");("2. Удаление данных о постояльце\n");("3. Просмотр всех зарегистрированных постояльцев\n");("4. Очистка данных о постояльцах\n");("5. Поиск постояльца по № паспорта\n");("6. Поиск постояльца по ФИО\n");("7. Добавление нового гостиничного номера \n");("8. Удаление сведений о гостиничном номере \n");("9. Просмотр всех имеющихся гостиничных номеров \n");("10. Очистка данных о гостиничных номерах\n");("11. Поиск гостиничного номера по '№ гостиничного номера'\n");("12. Поиск гостиничного номера по оборудованию\n");("13. Регистрация вселения постояльца\n");("14. Регистрация выселения постояльца\n");("0. Выход\n");

}main ()

{H, choice, step = 0;CountBads;CountFreeBads=-1;CountRooms;status=0;status2;Number2;startdate [11];enddate [11];toilet;tmp [4];Number [4];pass [12];equipment [1000];equip [1000];NODE *Tree = (struct NODE *) malloc (sizeof (struct NODE));= NULL;element *pbegin = create ();

/*******************************

инициализируем хеш-таблицу

********************************/hashkey=0;**hashtable= (NodeHash**) malloc (97*sizeof (NodeHash*));(int i=0; i<97; i++)

{[i] = (NodeHash*) malloc (sizeof (NodeHash));(hashtable [i] - >adress,"\0");[i] - >birthdayyear=0;(hashtable [i] - >fio,"\0");[i] - >next=0;(hashtable [i] - >passport,"0000-000000");(hashtable [i] - >target,"\0");

}

{("cls");();(stdin);=-1;("%i", &choice);(choice == 1)

{NodeHash *pv = new struct NodeHash;("cls");

{("\nВведите № паспорта: ");(stdin);("%s",&pv->passport);

}while (pars_passport (pv->passport));=hash (pv->passport);(find_repeats (hashtable,hashkey,pv->passport))

{("\nНевозможно зарегистрировать двух постояльцев с одинаковыми паспортами!");

_getch ();;

}{("\nВведите ФИО: ");(stdin);(pv->fio);

}while (pars_fio (pv->fio));{("\nВведите год рождения: ");(stdin);("%i",&pv->birthdayyear);

}while (pars_year (pv->birthdayyear));("\nВведите адрес: ");(stdin);(pv->adress);("\nВведите цель прибытия: ");(stdin);(pv->target);_hash_element (hashtable, pv, hashkey);("\nРегистрация завершена. ");

_getch ();;

}(choice == 2)

{("cls");{("\nВведите № паспорта: ");(stdin);("%s",&pass);

}while (pars_passport (pass));(check_reg (pbegin,pass))

{("\nНельзя удалить заселенного постояльца");

_getch ();;

}=hash (pass);_hash_element (hashtable,pass,hashkey);

_getch ();;

}(choice == 3)

{("cls");("Список постояльцев: \n");_hash_elements (hashtable);

_getch ();;

}(choice == 4)

{("cls");("Очистка данных о постояльцах");(strcmp (pbegin->passport,"ffff-ffffff")! =0)

{("\nБаза зарегистрированных постояльцев не пуста! \nОчистка данных о постояльцах невозможна!");

_getch ();;

}_hash_table (hashtable);("\nok");

_getch ();;

}(choice == 5)

{("cls");{("\nВведите № паспорта для поиска: ");(stdin);("%s",&pass);

}while (pars_passport (pass));_hash_table (hashtable,pass, pbegin);

_getch ();;

}(choice == 6)

{fio [100];("cls");{("\nВведите ФИО для поиска: ");(stdin);(fio);

}while (pars_fio (fio));_hash_table (hashtable,fio);

_getch ();;

}(choice == 7)

{

{("\nВведите № гостиничного номера\nl-люкс\np-полюлюкс\no-одноместный\nm-многоместный\n: > ");(stdin);("%s", &Number);

}while (pars_number (Number));(*Number=='o') // если выбран одноместный номер, то количество комнат и мест равно 1 по умолчанию=CountBads=1;

{

{(stdin);("\nВведите количество мест: ");("%s",&tmp);

}while (pars_num (tmp));=atoi (tmp);

{(stdin);("\nВведите количество комнат: ");("%s",&tmp);

}while (pars_num (tmp));=atoi (tmp);

}

{(stdin);("\nНаличие санузла (y/n): ");("%s",&tmp);

}while (pars_bool (tmp));=tmp [0];(stdin);("\nОборудование: ");(equipment);(stdin);= Binary_Tree (Number, CountBads, CountRooms, toilet,equipment, Tree, &H);;

}(choice == 8)

{("cls");{("\nВведите номер гостиничного номера для удаления (без типа): ");(stdin);("%s", &tmp);

}while (pars_num (tmp));(find_number (pbegin, tmp))

{("\nНельзя удалить номер, когда в нем зарегистрирован (ы) постоялец (ы)");

_getch ();;

}=atoi (tmp);=0;= Delete_Element (Tree, Number2, &H, &status);(status==1)("\n Запись удалена\n");

_getch ();;

}(choice == 9)

{("cls");("\nИнформация о гостиничных номерах: \n");(! Tree)

{("\nБаза пуста!");

_getch ();;

}(Tree);

_getch ();("cls");;

}(choice == 10)

{("cls");(strcmp (pbegin->passport,"ffff-ffffff")! =0)

{("\nНельзя очистить базу номеров, когда в них зарегистрированы постояльцы");

_getch ();;

}(&Tree);

// Tree=NULL;(Tree);("\nОчистка базы завершена\n");

_getch ();("cls");;

}(choice == 11)

{("cls");{("\nВведите № гостиничного номера: ");(stdin);(Number);

}while (pars_number (Number));=0;("Информация о номере: ");_hotel_room (Tree, Number, pbegin, hashtable,&status);(! status)("информация не найдена");

_getch ();;

}(choice == 12)

{("cls");("Введите необходимое оборудование: ");(stdin);(equip);=0;("Найденные номера: ");(Tree, equip, &status);(status==0)("Ничего не найдено");

_getch ();("cls");;

}(choice == 13)

{("cls");("Регистрация вселения постояльца \n");{("\nВведите № паспорта: ");(stdin);("%s", &pass);(strcmp (pass,"esc") ==0) break; // для выхода из ввода паспорта=0;=hash (pass);(pars_passport (pass))

{=0;;

}if (! find_repeats (hashtable,hashkey, pass))

{("Клиента нет в базе! Сначало зарегистрируйте постояльца, потом заселяйте! \nДля выхода из ввода наберите esc");=0;

}status=1;

}while (! status);(status==0) continue; // прерывание регистрации

{=0;("\nВведите № гостиничного номера: ");(stdin);(Number);(strcmp (Number,"esc") ==0) break; // для выхода из ввода гостиничного номера=0;(pars_number (Number))

{=0;;

}_numbers (Tree, Number, &status2, &CountFreeBads, 0);(! status2)

{("Номера нет в базе! Сначало добавьте гостиничный номер! \nДля выхода из ввода наберите esc");=0;;

}if (CountFreeBads<0)

{("В этом номере мест нет. \nДля выхода из ввода наберите esc");=0;;

}if (check_registration (pbegin, pass, Number))=0;status=1;

}while (! status);(status==0)

{_numbers (Tree, Number, &status2, &CountFreeBads, 1);; // прерывание регистрации

}

{("\nВведите дату заселения: ");(stdin);(startdate);

}while (pars_date (startdate));

{(stdin);("\nВведите дату выселения: ");(stdin);(enddate);

}while (pars_date (enddate));_registration (&pbegin, pass, Number,startdate,enddate);_sort (&pbegin);("\nok");

_getch ();("cls");;

}(choice == 14)

{("cls");("Регистрация выселения постояльца \n");(strcmp ( (pbegin) - >startdate,"00.00.0000") ==0)

{("Нет зарегистрированных клиентов. ");

_getch ();;

}

{("Введите № паспорта: ");(stdin);(pass);

}while (pars_passport (pass));

{("Введите номер в котором зарегистрирован постоялец: ");(stdin);(Number);

}while (pars_number (Number));_registration (Tree, &pbegin,pass,Number);;

}(choice == 0)(1);

}(1);

}

5. Тестирование программы


Главное окно программы


Регистрация нового постояльца


Просмотр всех зарегистрированных постояльцев


Поиск по номеру паспорта


Поиск по ФИО


Регистрация нового номера


Все гостиничные номера


Поиск номеров по фрагментам оборудования



Регистрация вселения постояльца


Регистрация выселения постояльца


Заключение


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

В курсовом проекте реализована информационная система регистрации постояльцев в гостинице.

Для организации данных использовались такие структуры данных, как хеш-таблицы, АВЛ-деревья, односвязные списки. Трудностей во время выполнения не возникло.

Список использованной литературы


1.      Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн, К. Алгоритмы: построение и анализ = Introduction to Algorithms / Под ред. И.В. Красикова. - 2-е изд. - М.: Вильямс, 2005. - 1296 с. - ISBN 5-8459-0857-4

2.      Вирт Н. Алгоритмы и структуры данных. М.: Мир, 1989.

.        Вирт H. Алгоритмы + структуры данных = программы. - М.: Мир, 1985. - 406 с.

.        М. Сибуя, Т. Ямомото. Алгоритмы обработки данных. - М.: "Мир", 1986.

Похожие работы на - Регистрация постояльцев в гостинице

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!