Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

СиАОД_1-4.docx

.pdf
Скачиваний:
5
Добавлен:
23.01.2025
Размер:
1.33 Mб
Скачать

// 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