Я знаю, що STL має API HashMap, але я не можу знайти жодної гарної та ретельної документації з хорошими прикладами щодо цього.
Будь-які добрі приклади будуть оцінені.
Я знаю, що STL має API HashMap, але я не можу знайти жодної гарної та ретельної документації з хорошими прикладами щодо цього.
Будь-які добрі приклади будуть оцінені.
Відповіді:
Стандартна бібліотека включає впорядковані та не упорядковані контейнери для карти ( std::map
та std::unordered_map
). У впорядкованій карті елементи сортуються за ключем, вставляють і доступ знаходиться у O (log n) . Зазвичай стандартна бібліотека внутрішньо використовує червоні чорні дерева для впорядкованих карт. Але це лише деталь реалізації. В невпорядковану карту вставити та отримати доступ у O (1). Це просто інша назва хештеля.
Приклад з (замовлений) std::map
:
#include <map>
#include <iostream>
#include <cassert>
int main(int argc, char **argv)
{
std::map<std::string, int> m;
m["hello"] = 23;
// check if key is present
if (m.find("world") != m.end())
std::cout << "map contains key world!\n";
// retrieve
std::cout << m["hello"] << '\n';
std::map<std::string, int>::iterator i = m.find("hello");
assert(i != m.end());
std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
return 0;
}
Вихід:
23 Ключ: привіт Значення: 23
Якщо вам потрібно замовити у вашому контейнері, і ви добре працюєте з O (log n), тоді просто використовуйте std::map
.
В іншому випадку, якщо ви дійсно потребуєте хеш-таблицю (O (1) вставки / доступ), перевірте std::unordered_map
, який має подібний до std::map
API (наприклад , в наведеному вище прикладі ви просто повинні знайти і замінити map
з unordered_map
).
unordered_map
Контейнер був введений з C ++ 11 стандартної версії. Таким чином, залежно від вашого компілятора, ви повинні включити функції C ++ 11 (наприклад, при використанні GCC 4.8 вам потрібно додати -std=c++11
до CXXFLAGS).
Ще до випуску C ++ 11 підтримується GCC unordered_map
- у просторі імен std::tr1
. Таким чином, для старих компіляторів GCC ви можете спробувати використовувати його так:
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
Він також є частиною прискорення, тобто ви можете використовувати відповідний заголовок підвищення для кращої портативності.
hash_map
unordered_map
. Таким чином, немає підстав вважати нестандартним hash_map
.
A hash_map
- це старіша, нестандартна версія того, що для цілей стандартизації називається unordered_map
(спочатку в TR1 і включене в стандарт з C ++ 11). Як випливає з назви, воно відрізняється std::map
головним чином від того, що це не впорядковано - якщо, наприклад, ви повторюєте карту від begin()
до end()
, ви отримуєте елементи в порядку за ключем 1 , але якщо ви повторюєте через unordered_map
від, begin()
до end()
, ви отримуєте елементи в більш-менш довільний порядок.
unordered_map
Зазвичай передбачається мати постійну складність. Тобто, вставка, пошук тощо, як правило, займає по суті фіксовану кількість часу, незалежно від кількості елементів у таблиці. Ця std::map
складність є логарифмічною щодо кількості елементів, що зберігаються - це означає, що час вставлення або отримання елемента зростає, але досить повільно , оскільки карта збільшується. Наприклад, якщо для пошуку одного з 1 мільйона елементів потрібно 1 мікросекунда, то для пошуку одного з 2 мільйонів елементів потрібно 3 мікросекунди, 3 мікросекунди для одного з 4 мільйонів елементів, 4 мікросекунди для одного з 8 мільйонів предметів тощо.
З практичної точки зору, це насправді не вся історія. За своєю природою проста хеш-таблиця має фіксований розмір. Пристосування його до вимог змінних розмірів для контейнера загального призначення дещо нетривіально. Як результат, операції, які (потенційно) збільшують таблицю (наприклад, вставка), потенційно відносно повільні (тобто більшість є досить швидкими, але періодично одна буде набагато повільнішою). Пошук, який не може змінити розмір таблиці, як правило, набагато швидший. Як результат, більшість таблиць на основі хешів, як правило, найкращі, коли ви робите велику кількість оглядів, порівняно з кількістю вставок. У випадках, коли ви вставляєте багато даних, потім повторіть таблицю один раз, щоб отримати результати (наприклад, підрахунок кількості унікальних слів у файлі), ймовірно, щоstd::map
буде настільки ж швидким і цілком можливо навіть швидшим (але, знову ж таки, складність обчислювань різна, так що також може залежати від кількості унікальних слів у файлі).
1 Якщо замовлення визначено третім параметром шаблону під час створення карти std::less<T>
за замовчуванням.
rehash
. Коли ви телефонуєте rehash
, ви вказуєте розмір таблиці. Цей розмір буде використовуватися, якщо це не перевищить вказаний максимальний коефіцієнт навантаження для таблиці (у цьому випадку розмір буде автоматично збільшуватися, щоб утримувати коефіцієнт навантаження в межах).
Ось більш повний і гнучкий приклад, який не опускає необхідних включень для створення помилок компіляції:
#include <iostream>
#include <unordered_map>
class Hashtable {
std::unordered_map<const void *, const void *> htmap;
public:
void put(const void *key, const void *value) {
htmap[key] = value;
}
const void *get(const void *key) {
return htmap[key];
}
};
int main() {
Hashtable ht;
ht.put("Bob", "Dylan");
int one = 1;
ht.put("one", &one);
std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}
Все ще не особливо корисно для ключів, якщо вони не визначені як покажчики, оскільки відповідне значення не буде робити! (Однак, оскільки я зазвичай використовую рядки для ключів, заміна "string" на "const void *" в декларації ключа повинна вирішити цю проблему.)
void*
. Для початку, немає ніяких причин обгортати те unordered_map
, що воно є частиною стандарту, і зменшує можливість ремонту коду. Далі, якщо наполягаєте на обгортанні, використовуйте templates
. Саме для цього вони і є.
Докази, що std::unordered_map
використовують хеш-карту в GCC stdlibc ++ 6.4
Про це згадувалося на веб- сайті: https://stackoverflow.com/a/3578247/895245, але у такій відповіді: Яка структура даних всередині std :: map в C ++? Я надав додаткові докази таких дій для реалізації GCC stdlibc ++ 6.4 шляхом:
Ось попередній графік характеристик ефективності, описаний у цій відповіді:
Як використовувати користувацький клас та хеш-функцію за допомогою unordered_map
Ця відповідь зазначає: C ++ unororder_map, використовуючи в якості ключа власний тип класу
Витяг: рівність:
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
Функція хешу:
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
}
Для тих із нас, хто намагається розібратися, як хешировать власні класи, використовуючи стандартний шаблон, є просте рішення:
У вашому класі потрібно визначити перевантаження оператора рівності ==
. Якщо ви не знаєте, як це зробити, у GeeksforGeeks є чудовий підручник https://www.geeksforgeeks.org/operator-overloading-c/
У стандартному просторі імен оголосіть структуру шаблону під назвою хеш із назвою вашого класу як тип (див. Нижче). Я знайшов чудовий поштовий блог, який також показує приклад обчислення хешів за допомогою XOR та бітшифтінга, але це поза межами цього питання, але воно також містить детальні інструкції щодо виконання хеш-функцій https://prateekvjoshi.com/ 2014/06/05 / за допомогою класів-хеш-функцій-в-с-для визначених користувачем класів /
namespace std {
template<>
struct hash<my_type> {
size_t operator()(const my_type& k) {
// Do your hash function here
...
}
};
}
std::map
або std::unordered_map
так, як ви зазвичай робите і використовувати my_type
в якості ключа, стандартна бібліотека автоматично використовувати хеш-функцію, яку ви визначили раніше (на кроці 2) ваші ключі.#include <unordered_map>
int main() {
std::unordered_map<my_type, other_type> my_map;
}