Реализация хеш-таблицы
Мне нужно реализовать хеш-таблицу и сравнить её с STL unordered_map. Мои показатели как-то сильно отличаются от unordered_map. Прошу подсказать, где я ошибся. Вот мой код:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <time.h>
#include <cstring>
#include <unordered_map>
using namespace std;
long long generateRandLong() { //генератор большого(12 цифрового) ключа для хеш-функции
int num = 12;
long long a = 1;
long long r = 0;
for (int i = 0; i < num; i++)
{
if (i == (num - 1))
{
r += a * ((long long)(rand() % 9) + 1);
break;
}
r += a * (rand() % 10);
a *= 10;
}
return r;
}
struct Data { // структура данных
char name[10];
int amountOfStudents;
bool turnstiles;
Data() { // генератор случайных данных для структуры Data
char* name1 = new char[10];
for (int i = 0; i < 9; i++)
{
name1[i] = 'a' + rand() % 26;
}
name1[9] = '\0';
strcpy(name, name1);
this->amountOfStudents = rand() % 30;
this->turnstiles = rand() % 2;
delete[] name1;
name1 = nullptr;
}
Data(const char a[], int b, bool c){ // конструктор Data
strncpy(name, a, 10);
amountOfStudents = b;
turnstiles = c;
}
};
struct HashNode { узел, необходимый для реализации односвязного списка
long long key;
Data data;
HashNode* next;
HashNode(Data data, long long key, HashNode* next) {
this->data = data;
this->key = key;
this->next = next;
}
};
struct LinkedList { // односвязный список для каждой ячейки в массиве
HashNode* head = NULL;
HashNode* tail = NULL;
void push_back(Data data, long long key) // метод, необходимый для "insert" в таблицу
{
HashNode* newHashNode = new HashNode(data, key, NULL);
if (head == NULL)
{
head = newHashNode;
tail = newHashNode;
}
else
{
tail->next = newHashNode;
tail = newHashNode;
}
}
};
struct HashTable { // сама хеш-таблица
int m = 8;
LinkedList* bucketsArray = new LinkedList[m];
const int alpha = 2;
int realsize = 0;
float LoadFactor = 3;
int hash(long long key) { // хеш-функция
//cout << key % m << endl;
return key % m;
}
void insert(long long key, Data value) // добавление элемента в таблицу
{
if (LoadFactor < ((float)realsize / m))
{
m *= alpha;
LinkedList* NewbucketsArray = new LinkedList[m];
for (int i = 0; i < m / alpha; i++)
{
NewbucketsArray[i] = bucketsArray[i];
}
delete[] bucketsArray;
bucketsArray = NewbucketsArray;
}
int index = hash(key);
HashNode* currentNode = bucketsArray[index].head;
while (currentNode != NULL) {
if (currentNode->key == key) {
currentNode->data = value;
return;
}
currentNode = currentNode->next;
}
bucketsArray[index].push_back(value, key);
realsize++;
}
Data* find(long long key) // поиск элемента по таблице
{
int index = hash(key);
HashNode* currentNode = bucketsArray[index].head;
if (currentNode == NULL)
{
//cout << "There is no such an element with such a key" << endl;
return NULL;
}
if (currentNode->key == key)
{
return ¤tNode->data;
}
while (currentNode != NULL)
{
if (currentNode->key == key)
return ¤tNode->data;
currentNode = currentNode->next;
}
return NULL;
}
Data erase(long long key) // удаление элемента из таблицы
{
int index = hash(key);
if(bucketsArray[index].head == NULL)
{
//cout << "There is no such an element in the list" << endl;
Data a("\0", 0, 0);
return a;
}
else
{
HashNode* tempNode = bucketsArray[index].head;
if (tempNode->key == key)
{
if (tempNode->next == NULL)
{
bucketsArray[index].head = NULL;
Data a = tempNode->data;
delete tempNode;
realsize--;
return a;
}
bucketsArray[index].head = bucketsArray[index].head->next;
Data a = tempNode->data;
delete tempNode;
realsize--;
return a;
}
HashNode* prevNode = bucketsArray[index].head;
HashNode* currentNode = bucketsArray[index].head->next;
while (currentNode != NULL)
{
if (currentNode->key == key)
break;
prevNode = currentNode;
currentNode = currentNode->next;
}
if (currentNode == NULL)
{
//cout << "There is no such an element with such a key" << endl;
Data a("\0", 0, 0);
return a;
}
prevNode->next = currentNode->next;
realsize--;
Data a = currentNode->data;
delete currentNode;
return a;
}
}
int size() // размер таблицы
{
return realsize;
}
};
bool testHashTable() // сравнение моей таблицы с unordered_map
{
const int iters = 500000;
const int keysAmount = iters * 1;
// generate random keys:
long long* keys = new long long[keysAmount];
long long* keysToInsert = new long long[iters];
long long* keysToErase = new long long[iters];
long long* keysToFind = new long long[iters];
for (int i = 0; i < keysAmount; i++)
{
keys[i] = generateRandLong();
}
for (int i = 0; i < iters; i++)
{
keysToInsert[i] = keys[generateRandLong() % keysAmount];
keysToErase[i] = keys[generateRandLong() % keysAmount];
keysToFind[i] = keys[generateRandLong() % keysAmount];
}
// test my HashTable:
HashTable hashTable;
clock_t myStart = clock();
for (int i = 0; i < iters; i++)
{
hashTable.insert(keysToInsert[i], Data());
}
int myInsertSize = hashTable.size();
for (int i = 0; i < iters; i++)
{
hashTable.erase(keysToErase[i]);
}
int myEraseSize = hashTable.size();
int myFoundAmount = 0;
for (int i = 0; i < iters; i++)
{
if (hashTable.find(keysToFind[i]) != NULL)
{
myFoundAmount++;
}
}
clock_t myEnd = clock();
float myTime = (float(myEnd - myStart)) / CLOCKS_PER_SEC;
// test STL hash table:
unordered_map<long long, Data> unorderedMap;
clock_t stlStart = clock();
for (int i = 0; i < iters; i++)
{
unorderedMap.insert({ keysToInsert[i], Data() });
}
int stlInsertSize = unorderedMap.size();
for (int i = 0; i < iters; i++)
{
unorderedMap.erase(keysToErase[i]);
}
int stlEraseSize = unorderedMap.size();
int stlFoundAmount = 0;
for (int i = 0; i < iters; i++)
{
if (unorderedMap.find(keysToFind[i]) != unorderedMap.end())
{
stlFoundAmount++;
}
}
clock_t stlEnd = clock();
float stlTime = (float(stlEnd - stlStart)) / CLOCKS_PER_SEC;
cout << "My HashTable:" << endl;
cout << "Time: " << myTime << ", size: " << myInsertSize << " - " << myEraseSize << ", found amount: " << myFoundAmount << endl;
cout << "STL unordered_map:" << endl;
cout << "Time: " << stlTime << ", size: " << stlInsertSize << " - " << stlEraseSize << ", found amount: " << stlFoundAmount << endl << endl;
delete [] keys;
delete [] keysToInsert;
delete [] keysToErase;
delete [] keysToFind;
if (myInsertSize == stlInsertSize && myEraseSize == stlEraseSize && myFoundAmount == stlFoundAmount)
{
cout << "Сompleted" << endl;
return true;
}
cerr << ":(" << endl;
return false;
}
int main() {
srand((unsigned)time(NULL));
testHashTable();
return 0;
}
Принцип работы: Создать специальный ключ для каждого элемента, найти индекс для вставки через хеш-функцию и через insert вставить элемент. Если LoadFactor(количество элементов в таблице разделенное на размер массива) меньше 3, то увеличить длину массива в 2 раза. Если под индексом в массиве есть уже данные, то проверить если ключ совпадает, то заменить старые на новые, если нет, то добавить элемент в односвязный список.