Метод кукушки (Cuckoo Hashing) - это современный метод хеширования, который решает проблему коллизий с использованием двух или более хеш-функций и обеспечивает постоянное время O(1) для операций поиска, вставки и удаления. Этот метод назван в честь поведения кукушек, откладывающих свои яйца в чужие гнезда, что отражает основную идею алгоритма: перемещение элементов при необходимости для освобождения места.
Принципы метода кукушки
Метод кукушки основывается на использовании двух или более хеш-функций и двух или более хеш-таблиц. Каждый элемент может быть помещен в одну из нескольких возможных позиций, определяемых этими хеш-функциями. При вставке нового элемента, если место занято, уже находящийся элемент перемещается в другую позицию, освобождая место для нового элемента.
Основные шаги метода кукушки
- Инициализация: Создаются две хеш-таблицы и две хеш-функции.
- Вставка:
- Вычисляются два возможных места для нового элемента с помощью двух хеш-функций.
- Если одно из мест свободно, элемент помещается туда.
- Если оба места заняты, один из существующих элементов вытесняется и перемещается в своё альтернативное место. Этот процесс повторяется рекурсивно.
- Поиск:
- Для элемента вычисляются два возможных места.
- Если элемент находится в одном из них, он возвращается. Если нет, возвращается значение “не найдено”.
- Удаление:
- Для элемента вычисляются два возможных места.
- Если элемент находится в одном из них, он удаляется. Если нет, возвращается значение “элемент отсутствует”.
Пример реализации на C++
Ниже приведен пример реализации метода кукушки на языке C++:
#include <iostream>
#include <vector>
#include <cstdlib> // Для функции rand
class CuckooHashTable {
private:
std::vector<int> table1, table2;
int size;
const int MAX_ITERATIONS = 50;
int hash1(int key) {
return key % size;
}
int hash2(int key) {
return (key / size) % size;
}
void rehash() {
std::vector<int> oldTable1 = table1;
std::vector<int> oldTable2 = table2;
table1.clear();
table2.clear();
table1.resize(size, -1);
table2.resize(size, -1);
for (int key : oldTable1) {
if (key != -1) {
insert(key);
}
}
for (int key : oldTable2) {
if (key != -1) {
insert(key);
}
}
}
public:
CuckooHashTable(int size) : size(size) {
table1.resize(size, -1);
table2.resize(size, -1);
}
void insert(int key) {
int currentKey = key;
int table = 1;
for (int i = 0; i < MAX_ITERATIONS; ++i) {
if (table == 1) {
int pos = hash1(currentKey);
if (table1[pos] == -1) {
table1[pos] = currentKey;
return;
} else {
std::swap(currentKey, table1[pos]);
table = 2;
}
} else {
int pos = hash2(currentKey);
if (table2[pos] == -1) {
table2[pos] = currentKey;
return;
} else {
std::swap(currentKey, table2[pos]);
table = 1;
}
}
}
rehash();
insert(currentKey);
}
bool search(int key) {
int pos1 = hash1(key);
if (table1[pos1] == key) {
return true;
}
int pos2 = hash2(key);
if (table2[pos2] == key) {
return true;
}
return false;
}
void remove(int key) {
int pos1 = hash1(key);
if (table1[pos1] == key) {
table1[pos1] = -1;
return;
}
int pos2 = hash2(key);
if (table2[pos2] == key) {
table2[pos2] = -1;
return;
}
}
void print() {
std::cout << "Table 1: ";
for (int key : table1) {
if (key != -1) {
std::cout << key << " ";
} else {
std::cout << "- ";
}
}
std::cout << "\nTable 2: ";
for (int key : table2) {
if (key != -1) {
std::cout << key << " ";
} else {
std::cout << "- ";
}
}
std::cout << std::endl;
}
};
int main() {
CuckooHashTable hashTable(11);
hashTable.insert(10);
hashTable.insert(20);
hashTable.insert(30);
hashTable.insert(40);
hashTable.insert(50);
hashTable.insert(60);
hashTable.print();
std::cout << "Search 30: " << (hashTable.search(30) ? "Found" : "Not found") << std::endl;
std::cout << "Search 70: " << (hashTable.search(70) ? "Found" : "Not found") << std::endl;
hashTable.remove(30);
hashTable.print();
std::cout << "Search 30: " << (hashTable.search(30) ? "Found" : "Not found") << std::endl;
return 0;
}
Преимущества метода кукушки
- Постоянное время операций: Операции вставки, поиска и удаления выполняются за O(1) в среднем случае.
- Простота: Алгоритм относительно прост в реализации и понимании.
- Отсутствие цепочек: В отличие от метода цепочек, метод кукушки не использует списков, что упрощает управление памятью.
Недостатки метода кукушки
- Рехеширование: В случае многократных коллизий алгоритм может потребовать рехеширования, что временно увеличивает затраты на вставку.
- Потребление памяти: Метод требует две или более таблицы, что увеличивает потребление памяти по сравнению с методами, использующими одну таблицу.
Метод кукушки является эффективным и надежным методом хеширования, который находит широкое применение в различных областях, где требуется высокая производительность операций с данными.