Исходный код основной программы

Лабораторная работа №1

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

Вариант 1

 

 

Выполнили:

Займак Д.В.

Ведмеденко Д.С.

Коляда И.С.

Группа: ЗФ-322

Проверила: Романенко Т.А.

Новосибирск

2016

 

ЗАДАНИЕ

1. Спроектировать, реализовать и провести тестовые испытания АТД «Хеш-таблица» для коллекции, содержащей объекты произвольного типа. Тип объектов задаётся клиентской программой.

АТД «Хеш-таблица» представляет ассоциативное, неупорядоченное множество элементов, доступ к которым выполняется с использованием ключа.

Коллекция проектируется в одном из двух вариантов:

· АТД «Хеш-таблица с цепочками коллизий»,

· АТД «Хеш-таблица с открытой адресацией»,

Интерфейс АТД «Хеш-таблица» включает следующие операции:

· опрос размера таблицы,

· опрос количества элементов в таблице,

· опрос пустоты таблицы,

· очистка таблицы,

· поиск элемента по ключу,

· вставка элемента по ключу,

· удаление элемента по ключу.

· итератор для доступа к элементам в таблице с операциями:

- установка на первый элемент в таблице,

- переход к следующему элементу в таблице,

- проверка окончания просмотра,

- доступ по чтению и записи к данным текущего элемента.

Для тестирования коллекций интерфейс АТД «Хеш-таблица» включает дополнительные операции:

· вывод структуры хеш-таблицы на экран,

· опрос числа проб, выполненных операцией.

2. Выполнить отладку и тестирование всех операций АТД «Хеш-таблица с цепочками коллизий» и АТД «Хеш-таблица с открытой адресацией» с помощью меню операций.

3. Получить экспериментальную оценку качества хеш-функции c 2, усреднённую по нескольким экспериментам.

4. Выполнить сравнительное тестирование средней трудоёмкости операций поиска, вставки и удаления для коллекций «Хеш-таблица с цепочками коллизий», и «Хеш-таблица с открытой адресацией» в зависимости от коэффициента заполнения α.

5. Провести сравнительный анализ теоретических и экспериментальных показателей трудоёмкости операций.

6. Составить отчёт по лабораторной работе. Отчёт должен содержать следующие пункты:

1) пункты 1 – 5 (см. раздел 2.2),

6) описание методики получения экспериментальной оценки c 2 и полученная оценка c 2, усреднённая по нескольким экспериментам.

7) описание методики тестирования трудоёмкости операций,

8) таблицы и графики с полученными оценками трудоёмкости операций. Должны быть приведены графики трудоёмкости для операций поиска, вставки и удаления для АТД «Хеш-таблица с цепочками коллизий» и АТД «Хеш-таблица с открытой адресацией»,

9) сравнительный анализ теоретических и экспериментальных оценок эффективности операций АТД,

10) выводы,

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

12) приложение с текстами программ:

- полное определение классов и текстов методов класса,

- текст программы получения оценки c 2,

текст программы тестирования трудоёмкости операций АТД.

Вариант 1.

Форма представления: хеш – таблица с цепочками коллизий

Тип ключа - вещественное число на интервале [-10 000.00, +10 000.00].

Метод хеширования - мультипликативный.

 

ФОРМАТ АТД

АТД «Хэш-таблица с цепочками коллизий »

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

Данные:

Параметры:

Размер хэш-таблицы

Структура хранения коллекции:

Статический массив элементов, содержащих ключ, значение и указатель на следующий элемент.

 

ОПЕРАЦИИ:

 

Конструктор:

Вход: количество элементов

Предусловия: не создана хэш таблица

Процесс: создание хэш-таблицы

Выход: нет

Постусловия: создана хэш таблица

Деструктор:

Вход: нет

Предусловия: создана хэш таблица

Процесс: удаление хэш таблицы

Выход: нет

Постусловия: создана хэш таблица

 

опрос размера таблицы:

Вход: нет

Предусловия: нет

Процесс: возвращение размера таблицы

Выход: размер таблицы

Постусловия: нет

 

опрос количества элементов в таблице:

Вход: нет

Предусловия: нет

Процесс: возвращение количества элементов

Выход: количество элементов

Постусловия: нет

опрос пустоты таблицы:

Вход: нет

Предусловия: нет

Процесс: проверка заполненности таблицы

Выход: Истина, если таблица заполнена, иначе Ложь.

Постусловия: нет

 

очистка таблицы:

Вход: нет

Предусловия: нет

Процесс: удаление всех элементов

Выход: нет

Постусловия: таблица пуста

 

поиск элемента по ключу:

Вход: ключ элемента

Предусловия: элемент существует

Процесс: поиск элемента

Выход: значение элемента.

Постусловия: исключение при невыполнении предусловия

 

вставка элемента по ключу:

Вход: ключ элемента

Предусловия: нет

Процесс: поиск элемента

Выход: Истина, если элемент вставлен, иначе Ложь.

Постусловия: элемент добавлен

удаление элемента по ключу:

Вход: ключ элемента

Предусловия: элемент существует

Процесс: поиск элемента, удаление.

Выход: нет.

Постусловия: элемент удален. Исключение, если предусловие не выполнено.

КОНЕЦ АТД

Описание класса

class HashTab {

 

// Элемент списка, который содержится в хэш-таблице

struct Element {

   K key; // Ключ

   V value; // Значение

   Element *next; // Следующий элемент списка

};

 

// Массив

Element **array;

// Размер хэш-таблицы

int capacity;

// Количество элементов в таблице

int size;

 

// Функция хэширования

int hash(K key);

 

int counter;

 

// Поиск элемента по ключу

Element *find(K key);

int closest_pow2 (int x);

 

public:

 

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

HashTab(int capacity = 8);

// Деструктор

~HashTab();

     

// Добавляет элемент

bool insert(K key, V value);

// Удаляет элемент

bool remove(K key);

 

// Возвращает значение

V& get_value(K key);

 

// Возвращает количество элементов

int get_size();

// Возвращает размер хэш-таблицы

int get_capacity();

// Определяет пуста ли хэш-таблица

bool is_empty();

// Очищает хэш-таблицу

void clear();

int get_counter();

 

//Класс итератора

class Iterator {

   HashTab<K, V> *table;

   bool off;

   int index;

   Element *pointer;

public:

   Iterator(HashTab<K, V> &table);

   V& operator*();

   bool beg();

   bool is_off();

   bool next();

   K get_key();

};

 

// Вывод хэш таблицы на экран

void print();

};

Вывод

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

 

 



Приложение А

Исходный код основной программы

#include "HashTab.h"

#include <cstdlib>

#include <conio.h>

#include <iostream>

#include <time.h>

using namespace std;

 

void test_rand(int, double);

 

int main(int argc, char *argv[]) {

  setlocale (0, "Rus");

 

//Переменные

int n = 3, value;

double key;

//Строки

const char wait_message[] = "Для продолжения нажмите любую клавишу...\n";

  const char iterator_out_error[] = "Ошибка: итератор за пределами коллекции!\n";

  const char key_not_found[]= "Ошибка: введённый ключ отсутствует в коллекции!\n";

 

HashTab<double, int>::Iterator *iterator;

 

  // Очистка экрана

system("cls");

  while(n!= 2) {

        cout << "Итератор:\n"

   "1. Тестирование\n"

        "2. Меню\n"

        "0. Выход\n";

        cin >> n;

        switch(n) {

               int sz;

               double a;

       case 1:

                      cout << "Введите кол-во элементов:";

                      cin >> sz;

                      cout << "Введите альфу:";

                      cin >> a;

                      test_rand(sz, a);

                      break;

 

               case 0: return 0;

        }

  }

system("cls");

//Запрос размера

cout << "Количество элементов: ";

cin >> n;

 

system("cls");

 

//Создаём таблицу

HashTab<double, int> *tab = new HashTab<double, int>(n);

 

 

//Основной цикл меню

bool iterator_created = false;

while(true) {

   //system("cls");

 

   //Отобразить коллекцию

   cout << "----------------------Хэш таблица--------------------------\n";

   tab->print();

 

   //Отобразить итератор

   if(iterator_created) {

       cout << "----------------------Итератор-------------------------\n";

       if(!iterator->is_off())

           cout << "\tКлюч: " << iterator->get_key() << "\tЗначение: " << **iterator << '\n';

       else

              cout << "\tКлюч: -\tЗначение: -\n";

   }

   cout << "*************************************************\n";

 

   // Меню

   if(iterator_created) {

       cout << "Итератор:\n"

               "1. Сбросить\n"

               "2. Следующий элемент\n"

               "3. Вывести значение\n"

               "4. Изменить значение\n"

               "5. Состояние итератора\n"

               "6. Удалить итератор\n";

   } else {

       cout << "Действия:\n"

               "1. Вставить значение\n"

               "2. Удалить значение\n"

               "3. Вывести значение\n"

               "4. Изменить значение\n"

               "5. Количество элементов\n"

               "6. Очистить таблицу\n"

               "7. Проверить на пустоту\n"

               "8. Создать итератор\n"

                             "9. Размер таблицы\n";

   }

   cout << "0. Выход\n*************************************************\n";

 

   //Выбор пункта и проверка на корректность

   cin >> n;

   if(n < 0 || (!iterator_created && n > 11) || (iterator_created && n > 9))

       continue;

 

   //Код пунктов меню

   switch(n) {

       case 1: //Вставка | Сброс итератора

            if(iterator_created) {

               iterator->beg();

           } else {

               cout << "Введите ключ и значение: ";

               cin >> key >> value;

               if(tab->insert(key, value) == 0) {

                  cout << "Ошибка: коллекция уже содержит идентичный ключ!\n" << wait_message;

                   _getch();

               }

           }

       break;

       case 2: //Удаление по ключу | Итератор - следующее значение

           if(iterator_created) {

               if(!iterator->next()) {

                   cout << iterator_out_error << wait_message;

                   _getch();

               }

           } else {

               cout << "Введите ключ удаляемого элемента: ";

               cin >> key;

               if(tab->remove(key) == 0) {

                   cout << key_not_found << wait_message;

                   _getch();

               }

           }

       break;

       case 3: //Получить значение

           if(iterator_created) {

               try {

                   cout << "Значение = " << **iterator << '\n';

               } catch(exception) {

                   cout << iterator_out_error;

               }

           } else {

               cout << "Введите ключ элемента: ";

               cin >> key;

               try {

                   cout << tab->get_value(key) << '\n' << wait_message;

               }

               catch(exception) {

                   cout << key_not_found << wait_message;

               }

               _getch();

           }

       break;

       case 4: //Изменить значение

           if(iterator_created) {

               try {

                   cout << "Текущее значение = " << **iterator << '\n';

                   cout << "Изменить на ";

                   cin >> value;

                   **iterator = value;

               } catch(exception) {

                   cout << iterator_out_error << wait_message;

                   _getch();

               }

           } else {

               cout << "Введите ключ элемента и новое значение: ";

               cin >> key >> value;

               try {

                   tab->get_value(key) = value;

               }

               catch(exception) {

                   cout << key_not_found << wait_message;

                   _getch();

               }

           }

       break;

       case 5: //Число элементов | Состояние итератора

           if(iterator_created) {

               if(iterator->is_off())

                   cout << "Итератор вне коллекции\n";

               else

                   cout << "Итератор внутри коллекции\n";

               cout << wait_message;

               _getch();

           } else {

              cout << "Число элементов в таблице: " << tab->get_size() << '\n' << wait_message;

               _getch();

           }

       break;

       case 6: //Очистка | Удалить итератор

           if(iterator_created) {

                 if(iterator) {

                   delete iterator;

                             }

               iterator_created = 0;

           } else {

               tab->clear();

           }

       break;

       case 7: //Проверка на пустоту

           if(tab->is_empty()) {

               cout << "Коллекция пуста\n";

                      } else {

               cout << "Коллекция не пуста\n";

                      }

           cout << wait_message;

           _getch();

       break;

       case 8: //Создать итератор

           iterator_created = 1;

           iterator = new HashTab<double, int>::Iterator(*tab);

       break;

       case 9: //Размер таблицы

                      if(!iterator_created) {

                             cout << "Размер таблицы:" << tab->get_capacity() << endl;

                      }

       break;

       case 0: //Выход

           return 0;

       break;

   }

}

  return 0;

}

 

double lrand(){

  return ((float) rand() * (float) rand()) / 100.0;

}

 

void test_rand(int sz, double a){

  srand(time(NULL));

  HashTab<double, int> * tab = new HashTab<double, int>(sz);

  cout << "capacity: " << tab->get_capacity() << endl;

  int n = floor(tab->get_capacity()*a);

  double* m=new double[n];

 

  for(int i=0; i<n; i++)

  {

        m[i]=lrand();

        tab->insert(m[i],1);

  }

 

      

  double I=0;

  double D=0;

  double S=0;

  double idx;

 

  for(int i=0;i<n/2;i++)

        if(i%10==0){

                      idx = lrand();

                      tab->remove(idx);

                      D+=tab->get_counter();

                      tab->insert(m[rand()%n],1);

                      I+=tab->get_counter();

                      try{

                             tab->get_value(lrand());

                             S+=tab->get_counter();

                      }catch(exception& e){S+=tab->get_counter();}

               } else {

                      int ind=rand()%n;

                      idx = m[ind];

                      try{

                             tab->get_value(rand()%n);

                             S+=tab->get_counter();

               }catch(exception& e){

                             S+=tab->get_counter();

                      }

                          

                      tab->remove(idx);

                      D+=tab->get_counter();

                      double key=lrand();

                      tab->insert(key,1);

                      I+=tab->get_counter();

                      m[ind]=key;

 

 

        }

 

      

  cout<<"items count: "<<tab->get_size()<<endl;

  cout<<"Count insert: " << I/(n/2) <<endl;

  cout<<"Count delete: " << D/(n/2) <<endl;

  cout<<"Count search: " << S/(n/2) <<endl;

 

  delete[] m;

}

 

 

Приложение Б

Исходный код класса

 

#ifndef HASHTAB_H

#define HASHTAB_H

 

#include <iostream>

#include <cmath>

 

using namespace std;

 

 

template <class K, class V>

class HashTab {

 

  // Элемент списка, который содержится в хэш-таблице

struct Element {

   K key; // Ключ

   V value; // Значение

   Element *next; // Следующий элемент списка

};

 

  // Массив

Element **array;

  // Размер хэш-таблицы

int capacity;

  // Количество элементов в таблице

int size;

 

  // Функция хэширования

  int hash(K key);

 

  int counter;

 

  // Поиск элемента по ключу

  Element *find(K key);

  int closest_pow2 (int x);

 

public:

 

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

HashTab(int capacity = 8);

  // Деструктор

~HashTab();

      

  // Добавляет элемент

bool insert(K key, V value);

  // Удаляет элемент

bool remove(K key);

 

  // Возвращает значение

V& get_value(K key);

 

// Возвращает количество элементов

int get_size();

  // Возвращает размер хэш-таблицы

int get_capacity();

  // Определяет пуста ли хэш-таблица

bool is_empty();

  // Очищает хэш-таблицу

void clear();

  int get_counter();

 

//Класс итератора

class Iterator {

   HashTab<K, V> *table;

   bool off;

   int index;

   Element *pointer;

public:

   Iterator(HashTab<K, V> &table);

   V& operator*();

   bool beg();

   bool is_off();

   bool next();

   K get_key();

};

 

// Вывод хэш таблицы на экран

void print();

};

 

int round (double x) {

  return (int) floor(x + 0.5);

}

 

template <class K, class V>

int HashTab<K, V>::get_counter(){

  return counter;

}

template <class K, class V>

int HashTab<K, V>::hash(K key)

{

  double A = (sqrt(5.0) - 1) / 2;

  double tmp;

  double fract_part = modf(round(((key * 100)) + 1000000) * A, &tmp);

  return (int) floor(capacity * fract_part);

}

 

template <class K, class V>

typename HashTab<K, V>::Element* HashTab<K, V>::find(K key)

{

  counter = 1;

Element *cur = array[hash(key)];

while(cur) {

        ++counter;

   if(cur->key == key) {

       return cur;

        }

   cur = cur->next;

}

  return NULL;

}

template <class K, class V>

int HashTab<K, V>::closest_pow2(int x)

{

  int power = 1;

  while((power) <= x)

        power = power << 1;

  return power >> 1;

}

template <class K, class V>

HashTab<K, V>::HashTab(int capacity)

{

this->capacity = closest_pow2(capacity / 2);

if(this->capacity == 0)

   this->capacity = 1;

 

this->size = 0;

 

//Создаём массив элементов

array = new Element* [this->capacity];

 

  // Обнуляем массив

  memset(array, NULL, sizeof (Element *)* this->capacity);

}

 

template <class K, class V>

HashTab<K, V>::~HashTab()

{

clear();

delete []array;

}

 

template <class K, class V>

bool HashTab<K, V>::insert(K key, V value)

{

if (!find(key)) {

        int index = hash(key);

 

        Element *new_element = new Element();

        new_element->key = key;

        new_element->value = value;

        new_element->next = array[index];

        array[index] = new_element;

 

        ++size;

        return true;

  }

  return false;

}

 

template <class K, class V>

bool HashTab<K, V>::remove(K key)

{

int index = hash(key);

  counter = 1;

 

// Ищем элемент, который надо удалить

Element *prev = 0;

Element *cur = array[index];

while(cur!= 0) {

        ++counter;

   if(cur->key == key)

       break;

   prev = cur;

   cur = cur->next;

            

}

if(!cur) {

   return false;

  }

 

if(array[index] == cur) {

        //Удаляемый элемент первый в списке

   array[index] = cur->next;

} else {

        //Удаляемый не первый элемент

        prev->next = cur->next;

  }

delete cur;

--size;

return true;

}

 

template <class K, class V>

V& HashTab<K, V>::get_value(K key)

{

Element *ptr = find(key);

  if (ptr) {

        return ptr->value;

  } else {

        throw exception("There is no such element!");

  }

}

 

template <class K, class V>

int HashTab<K, V>::get_size()

{

return size;

}

 

template <class K, class V>

int HashTab<K, V>::get_capacity()

{

  return capacity;

}

 

template <class K, class V>

bool HashTab<K, V>::is_empty()

{

return size == 0;

}

 

template <class K, class V>

void HashTab<K, V>::clear()

{

for(int i = 0; i < capacity; ++i) {

   Element *cur = array[i];

   while(cur) {

       Element *next = cur->next;

       delete cur;

       cur = next;

   }

   array[i] = NULL;

}

  size = NULL;

}

 

template <class K, class V>

void HashTab<K, V>::print()

{

Element *cur;

for(int i = 0; i < capacity; ++i) {

   cout << i << ". ";

   cur = array[i];

   while(cur) {

       cout << cur->key << " ";

       cur = cur->next;

   }

   cout << '\n';

}

}

 

// Итератор

 

template <class K, class V>

HashTab<K, V>::Iterator::Iterator(HashTab<K, V> &table)

{

this->table = &table;

beg();

}

 

template <class K, class V>

V& HashTab<K, V>::Iterator::operator*()

{

if(off) {

   throw exception("The iterator is off!");

  }

return pointer->value;

}

 

template <class K, class V>

bool HashTab<K, V>::Iterator::beg()

{

//Если в таблице нет элементов - указатель не установлен

if(table->size == 0) {

   off = true;

   return false;

}

 

//Ищем первый элемент

off = false;

index = 0;

while(table->array[index] == 0) {

   ++index;

  }

pointer = table->array[index];

return true;

}

 

template <class K, class V>

bool HashTab<K, V>::Iterator::is_off()

{

return off;

}

 

template <class K, class V>

bool HashTab<K, V>::Iterator::next()

{

//Проверка на нахождение в коллекции

if(off) {

   return false;

  }

 

//Ищем следующий элемент в списке

if(pointer->next) {

   pointer = pointer->next;

   return true;

}

 

//Ищем следующий список

++index;

  while(index!= table->capacity && table->array[index] == 0) {

   ++index;

  }

 

//Вышли за пределы

if(index == table->capacity) {

   off = true;

   return true;

}

 

//Список найден

pointer = table->array[index];

return true;

}

 

template <class K, class V>

K HashTab<K, V>::Iterator::get_key()

{

if(off) {

   throw exception("The iterator is off!");

  }

return pointer->key;

}

 

#endif // HASHTAB_H

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: