Кроме того, когда общее (или максимальное) количество элементов N известно, хэш-таблица может быть построена заранее.
гм.резерв(N); // Это по цепочке вызовет функцию rehash()...
Насколько мне известно, здесь само целое число может использоваться в качестве функции идентификации (хеш-функции) для хэш-таблицы.
Это верно и разумно в двух очень разных сценариях: 1) когда значения в значительной степени смежны с, возможно, несколькими отсутствующими значениями, или 2) когда значения совершенно случайны. Во многих других ситуациях вы можете столкнуться с чрезмерными коллизиями хеш-таблиц, если не предоставите осмысленную хеш-функцию.
Тогда есть ли разница между построенной хеш-таблицей и std::vector, в использовании памяти или в механизме/производительности поиска или в чем-то еще?
да. После вашего .reserve(N)
хеш-таблица выделяет непрерывный блок памяти (по сути, массив) как минимум для N
сегментов. Если мы рассмотрим реализацию GCC, N будет округлено до простого числа. Каждое ведро может хранить итератор в прямом списке из pair<int, data_type>
узлов.
Итак, если вы на самом деле поместите N записей в хеш-таблицу, у вас будет...
- массив из ›= N элементов размера
sizeof(forward-list-iterator)
- N выделений памяти ›=
sizeof(pair<int, data_type>) + sizeof(next-pointer/iterator for forward-list)
... в то время как vector
использует только около N * sizeof(data_type)
байтов памяти: потенциально небольшая часть памяти, используемой хэш-таблицей, и, поскольку вся память вектора для data_type
s непрерывна, вы, скорее всего, выиграете от кэширования ЦП элементы, соседние с тем, к которому вы пытаетесь получить доступ в данный момент, так что все они будут намного быстрее доступны позже.
С другой стороны, если вы не поместили много элементов в хеш-таблицу, то основное использование памяти — это массив сегментов, содержащих итераторы, которые обычно имеют размер указателей (например, 32 или 64 бита каждый), тогда как вектор data_type
- если вы reserve(N)
там тоже - уже будет выделено N * sizeof(data_type)
байтов памяти - для больших data_type
это может быть значительно больше, чем хеш-таблица. Тем не менее, вы часто можете выделить виртуальную память, и если вы не ошиблись со страницами памяти, так что им нужна физическая резервная память, нет никакого значимого использования памяти или потери производительности для вашей программы или компьютера. (По крайней мере, с 64-битными программами виртуальное адресное пространство практически не ограничено).
Если я знаю, что 3 ключа 0,5, 9987 наверняка используются, но ключи 1~9986 могут использоваться или не использоваться.
Если я знаю, что ни один ключ в наборе не будет больше 10000, то использование std::vector размера 10000 гарантирует временную сложность O(1) для доступа к случайным данным, но память будет потрачена впустую.
В этой ситуации дает ли std::unordered_map лучшее решение проблемы? * Я имею в виду решение, которое экономит как можно больше памяти, сохраняя временную сложность на том же уровне.
В этой ситуации, если вы reversed(10000)
заранее и data_type
не намного больше, чем итератор/указатель, то unordered_map
однозначно будет хуже во всех отношениях. Если вы не резервируете заранее, хеш-таблица будет выделять место только для нескольких сегментов, и вы будете использовать гораздо меньше виртуального адресного пространства, чем vector
с 10000 элементами (даже если data_type
было bool
).
person
Tony Delroy
schedule
25.09.2020
std::vector
размера=10000
, но для пространственной сложности это наихудшая. Так что будет лучше, если новое решение распределит их примерно 7:3, так как в моем поле память менее важна, чем скорость. Но я уверен, что многому научился бы из любого нового решения проблемы. - person Moon   schedule 25.09.2020