Я пытаюсь использовать пользовательский класс в качестве ключа для unordered_map
, как показано ниже:
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
class node;
class Solution;
class Node {
public:
int a;
int b;
int c;
Node(){}
Node(vector<int> v) {
sort(v.begin(), v.end());
a = v[0];
b = v[1];
c = v[2];
}
bool operator==(Node i) {
if ( i.a==this->a && i.b==this->b &&i.c==this->c ) {
return true;
} else {
return false;
}
}
};
int main() {
unordered_map<Node, int> m;
vector<int> v;
v.push_back(3);
v.push_back(8);
v.push_back(9);
Node n(v);
m[n] = 0;
return 0;
}
Однако g ++ дает мне следующую ошибку:
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from 3sum.cpp:4:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Node]’:
/usr/include/c++/4.6/bits/hashtable_policy.h:768:48: instantiated from ‘bool std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_M_compare(const _Key&, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type, std::__detail::_Hash_node<_Value, false>*) const [with _Key = Node, _Value = std::pair<const Node, int>, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable.h:897:2: instantiated from ‘std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node* std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_M_find_node(std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node*, const key_type&, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type) const [with _Key = Node, _Value = std::pair<const Node, int>, _Allocator = std::allocator<std::pair<const Node, int> >, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, _Hash = std::__detail::_Default_ranged_hash, _RehashPolicy = std::__detail::_Prime_rehash_policy, bool __cache_hash_code = false, bool __constant_iterators = false, bool __unique_keys = true, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node = std::__detail::_Hash_node<std::pair<const Node, int>, false>, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::key_type = Node, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable_policy.h:546:53: instantiated from ‘std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type& std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::operator[](const _Key&) [with _Key = Node, _Pair = std::pair<const Node, int>, _Hashtable = std::_Hashtable<Node, std::pair<const Node, int>, std::allocator<std::pair<const Node, int> >, std::_Select1st<std::pair<const Node, int> >, std::equal_to<Node>, std::hash<Node>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>, std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type = int]’
3sum.cpp:149:5: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:209:23: error: passing ‘const Node’ as ‘this’ argument of ‘bool Node::operator==(Node)’ discards qualifiers [-fpermissive]
make: *** [threeSum] Error 1
Я думаю, мне нужно сказать C ++, как хэшировать класс Node
, однако я не совсем уверен, как это сделать. Как я могу выполнить эту задачу?
Ответы:
Чтобы иметь возможность использовать
std::unordered_map
(или один из других неупорядоченных ассоциативных контейнеров) с определяемым пользователем типом ключа, вам необходимо определить две вещи:Хэш - функция ; это должен быть класс, который переопределяет
operator()
и вычисляет значение хеш-функции с учетом объекта типа ключа. Один из наиболее простых способов сделать это - специализироватьstd::hash
шаблон для вашего типа ключа.Функция сравнения на равенство ; это необходимо, поскольку хеш-функция не может полагаться на тот факт, что хеш-функция всегда будет предоставлять уникальное хеш-значение для каждого отдельного ключа (т. е. она должна иметь возможность обрабатывать коллизии), поэтому ей необходим способ сравнения двух заданных ключей. для точного соответствия. Вы можете реализовать это либо как класс, который переопределяет
operator()
, либо как специализацияstd::equal
, либо - что проще всего - путем перегрузкиoperator==()
для вашего типа ключа (как вы уже сделали).Сложность с хэш-функцией заключается в том, что если ваш тип ключа состоит из нескольких элементов, обычно хеш-функция рассчитывает значения хеш-функции для отдельных элементов, а затем каким-то образом объединяет их в одно хеш-значение для всего объекта. Для обеспечения хорошей производительности (т. Е. Нескольких коллизий) вы должны тщательно продумать, как комбинировать отдельные значения хеш-функции, чтобы избежать слишком частого получения одинакового вывода для разных объектов.
Достаточно хорошей отправной точкой для хеш-функции является та, которая использует битовое смещение и битовое XOR для объединения отдельных хеш-значений. Например, предполагая тип ключа, подобный этому:
Вот простая хеш-функция (адаптированная из той, которая использовалась в примере cppreference для пользовательских хеш-функций ):
Имея это в
std::unordered_map
виду , вы можете создать экземпляр типа ключа:Он будет автоматически использоваться,
std::hash<Key>
как определено выше для вычислений хеш-значения, иoperator==
определен как функция-членKey
для проверок на равенство.Если вы не хотите специализировать шаблон внутри
std
пространства имен (хотя в данном случае это совершенно законно), вы можете определить хеш-функцию как отдельный класс и добавить ее в список аргументов шаблона для карты:Как определить лучшую хэш-функцию? Как сказано выше, определение хорошей хеш-функции важно, чтобы избежать коллизий и получить хорошую производительность. Для действительно хорошего вы должны принять во внимание распределение возможных значений всех полей и определить хеш-функцию, которая проецирует это распределение в пространство возможных результатов как можно более широкое и равномерное распределение.
Это может быть сложно; описанный выше метод XOR / битового сдвига, вероятно, неплохое начало. Для лучшего начала вы можете использовать шаблон функции
hash_value
иhash_combine
из библиотеки Boost. Первый действует аналогичноstd::hash
стандартным типам (в последнее время также включает кортежи и другие полезные стандартные типы); последнее помогает объединить отдельные значения хеш-функции в одно. Вот переписать хеш-функцию, которая использует вспомогательные функции Boost:И вот переписывание, которое не использует boost, но использует хороший метод объединения хэшей:
источник
KeyHasher
?std::hash
метод для моего ключа, как вы сделали. Я положил это в нижней части моего файла Key.cpp , но я получаю следующее сообщение об ошибке:Error 57 error C2440: 'type cast' : cannot convert from 'const Key' to 'size_t' c:\program files (x86)\microsoft visual studio 10.0\vc\include\xfunctional
. Я предполагаю, что компилятор не находит мой метод хеширования? Должен ли я что-нибудь добавить в мой файл Key.h?std::hash
на самом деле это не структура, а шаблон (специализация) для структуры . Так что это не реализация - она будет превращена в реализацию, когда это понадобится компилятору. Шаблоны всегда должны идти в заголовочные файлы. См. Также stackoverflow.com/questions/495021/…find()
возвращает итератор, и этот итератор указывает на «запись» карты. Записьstd::pair
состоит из ключа и значения. Так что если вы это сделаетеauto iter = m6.find({"John","Doe",12});
, вы получите ключiter->first
и значение (то есть строку"example"
) вiter->second
. Если вы хотите получить строку напрямую, вы можете использовать ееm6.at({"John","Doe",12})
(которая вызовет исключение, если ключ неm6[{"John","Doe",12}]
завершится ), или (которая создаст пустое значение, если ключ не существует).Я думаю, джогоджапан дал очень хороший и исчерпывающий ответ . Вы обязательно должны взглянуть на это, прежде чем читать мой пост. Однако я хотел бы добавить следующее:
unordered_map
отдельно, вместо использования оператора сравнения равенства (operator==
). Это может быть полезно, например, если вы хотите использовать последний для сравнения всех членов двухNode
объектов друг с другом, но только некоторые конкретные члены в качестве ключаunordered_map
.В целом, для вашего
Node
класса код может быть написан следующим образом:Ноты:
источник
unordered_map
конструктора .8
Представляет собой так называемый «счетчик ведро». Bucket - это слот во внутренней хеш-таблице контейнера, см., Например,unordered_map::bucket_count
дополнительную информацию.8
наугад. В зависимости от содержимого, которое вы хотите сохранитьunordered_map
, количество сегментов может повлиять на производительность контейнера.