
СиАОД_1-4.docx
.pdf// simple hash function
template <class Key> class Hash { public:
std::size_t operator()(const Key &key) const { std::size_t hash = 0;
for (char c : key) { hash = hash * 31 + c;
}
return hash;
}
};
template <class Key_type> class Node {
public:
Key_type s_key;
Node *next{nullptr};
explicit Node(Key_type &&key_)
: s_key(std::forward<Key_type>(key_)), next(nullptr) {}
};
template <class Key> class List { Node<Key> *head{nullptr};
public:
bool insert(Key &&insetr_key) {
if (head == nullptr) { std::cout << "simple insert "
<< "key=" << insetr_key << std::endl;
head = new Node<Key>(std::forward<Key>(insetr_key)); return true;
}
if (head->s_key == insetr_key) {
21
std::cout << "Key collision "
<< "key=" << insetr_key << "this " << this << std::endl; return false;
}
Node<Key> *turget = head;
while (turget->next != nullptr) {
turget = turget->next;
if (turget->s_key == insetr_key) { std::cout << "Key collision "
<< "key=" << insetr_key << " this " << this << std::endl; return false;
}
}
std::cout << "List double hash "
<< "key=" << insetr_key << std::endl;
turget->next = new Node<Key>(std::forward<Key>(insetr_key)); return true;
}
Node<Key> *find(Key &&key) const {
Node<Key> *turget = head;
while (turget != nullptr) {
if (turget->s_key == key) { return turget;
}
turget = turget->next;
}
return nullptr;
}
bool contains(Key &&key) const {
22
Node<Key> *turget = head; while (turget != nullptr) {
if (turget->s_key == key) { return true;
}
turget = turget->next;
}
return false;
}
~List() {
Node<Key> *current = head; while (current != nullptr) {
Node<Key> *next = current->next; delete current;
current = next;
}
}
};
template <class Key, class Hash = std::hash<Key>> class hash_table {
private:
List<Key> *table{nullptr}; std::size_t capasity{0}; std::size_t elem_count{0};
public:
hash_table() = delete;
hash_table(std::size_t capasity_) : capasity(capasity_) {
// load mem;
table = new List<Key>[capasity];
}
23
std::size_t size() const { return elem_count; }
bool insert(Key &&key) { std::size_t hash = Hash()(key); std::size_t index = hash % capasity;
if (table[index].insert(std::forward<Key>(key))) { ++elem_count;
return true; } else { return false;
}
}
bool contains(Key &&key) const { std::size_t hash = Hash()(key); std::size_t index = hash % capasity;
return table[index].contains(std::forward<Key>(key));
}
};
#endif // HASHTABLE_HPP
Были получены реализации красно-черного дерева и хеш таблицы с разрешением коллизий с помощью метода цепочек.
Приведем текст функции main, в котором будет показан принцип заполнения данными и замеров времени.
int main() {
long element_size = 1 << 20;
std::cout << "element size is " << element_size << std::endl;
std::vector<long> data(element_size); std::iota(data.begin(), data.end(), 0); std::cout << data.size() << std::endl;
24
std::random_device rd; std::mt19937 g(rd());
std::shuffle(data.begin(), data.end(), g);
#if debug
for (auto i : data) { std::cout << i << " ";
}
#endif
std::cout << "HASH" << std::endl; Timer timer_insert_hash; timer_insert_hash.start(); hash_table<long> ht(element_size / 4);
for (auto &el : data) { ht.insert(el);
}
timer_insert_hash.stop();
std::cout << " ht size " << ht.size() << " capacity " << ht.capacity()
<<" load factor " << ht.size() / ht.capacity() << std::endl; std::cout << "time create insert << " << element_size << " elements is "
<<timer_insert_hash.elapsedMicrosec() << " microseconds"
<<std::endl;
Timer timer_find_hash; timer_find_hash.start();
for (long i = 0; i < element_size; ++i) { if (not ht.contains(i)) {
std::cout << "error" << std::endl;
}
}
timer_find_hash.stop();
25
std::cout << "time find " << element_size << " elements is "
<<timer_find_hash.elapsedMicrosec() << " microseconds"
<<std::endl;
std::cout << " RB_TREE" << std::endl; Timer time_rb_tree_create_insert; time_rb_tree_create_insert.start(); rb_tree<long> tree;
for (auto &el : data) { tree.insert(el);
}
time_rb_tree_create_insert.stop();
std::cout << "time create insert " << element_size << " elements is "
<<time_rb_tree_create_insert.elapsedMicrosec() << " microseconds"
<<std::endl;
Timer time_rb_tree_find; time_rb_tree_find.start(); for (auto &el : data) {
if (not tree.contains(el)) { std::cout << "error" << std::endl;
}
}
time_rb_tree_find.stop();
std::cout << "time find " << element_size << " elements is " << time_rb_tree_find.elapsedMicrosec() << " microseconds" << std::endl;
std::cout << "std::unordered_set" << std::endl;
Timer std_unordered_set_insert; std_unordered_set_insert.start(); std::unordered_set<long> un_set; un_set.reserve(element_size / 2);
26
for (auto &el : data) { un_set.insert(el);
}
std_unordered_set_insert.stop();
std::cout << "time create insert " << element_size << " elements is " << std_unordered_set_insert.elapsedMicrosec() << " microseconds" << std::endl;
Timer std_unordered_set_find; std_unordered_set_find.start(); for (auto &el : data) {
/*
if (set.find(el) == set.end()) { std::cout << "error" << std::endl; } */
if( not un_set.contains(el)) { std::cout << "error" << std::endl;
}
}
std_unordered_set_find.stop();
std::cout << "time find " << element_size << " elements is " << std_unordered_set_find.elapsedMicrosec() << " microseconds" << std::endl;
std::cout << "std::set" << std::endl; Timer std_set_insert; std_set_insert.start(); std::set<long> set;
for (auto &el : data) { set.insert(el);
}
std_set_insert.stop();
std::cout << "time create insert " << element_size << " elements is " << std_set_insert.elapsedMicrosec() << " microseconds" << std::endl;
Timer std_set_find; std_set_find.start(); for (auto &el : data) {
if( not set.contains(el)) {
27
std::cout << "error" << std::endl;
}
}
std_set_find.stop();
std::cout << "time find " << element_size << " elements is " << std_set_find.elapsedMicrosec() << " microseconds" << std::endl;
std::cout << "END " << std::endl;
}
28
Замеры времени
Программа скомпилирована компилятором clang 19 с оптимизациями -02
lement size is 1048576 1048576
*************************
HASH
ht size 1048576 capacity 524288 load factor 2
time create insert << 1048576 elements is 117844 microseconds time find 1048576 elements is 21682 microseconds
*************************
RB_TREE
time create insert 1048576 elements is 899662 microseconds time find 1048576 elements is 210059 microseconds
*************************
std::unordered_set
time create insert 1048576 elements is 166424 microseconds time find 1048576 elements is 29179 microseconds
*************************
std::set
time create insert 1048576 elements is 795052 microseconds time find 1048576 elements is 163288 microseconds
*************************
VECTOR FIND NOT SORT
time find 1000 elements is 191762mosec
*************************
VECTOR FIND SORT
time sort and find 1000 elements is 69348 microseconds END
29
Выводы
По полученным данным можно сделать вывод что хеш таблица, является более приоритетной структурой данных вставка в нее выполняется быстрее чем в бинарное дерево , и поиск осуществляется быстрее. Средняя временная сложность хэш-таблицы для операций поиска,
вставки и удаления составляет O(1). Однако в худшем случае временная сложность для этих операций может составлять O(n), если в хэш-таблице много коллизий. Количество коллизий зависит от качества хэш-функции, количества элементов в хэш-таблице и распределения этих элементов по различным сегментам.
Двоичное красно-черное дерево имеет наихудшую временную сложность O(log n) для операций поиска, вставки и удаления. Это связано с тем, что красно-черное дерево является самобалансирующимся двоичным деревом поиска, которое поддерживает баланс между левым и правым поддеревьями. Этот баланс гарантирует, что высота дерева будет как можно меньше, что, в свою очередь, гарантирует сохранение временной сложности операций на уровне O(log n).
Таким образом, хэш-таблица имеет лучшую среднюю временную сложность для операций поиска, вставки и удаления, чем двоичное красно-черное дерево, но она имеет худшую временную сложность в наихудшем случае, когда возникает много коллизий. Двоичное красно-черное дерево, с другой стороны, имеет постоянную временную сложность в наихудшем случае, равную O(log n) для этих операций, но требует больше памяти для поддержания баланса между левым и правым под деревьями.
30