Хеш-таблица полностью работает на том факте, что практические вычисления следуют модели машины с произвольным доступом, то есть значение по любому адресу в памяти может быть получено за O (1) или постоянное время.
Итак, если у меня есть универсальный набор ключей (набор всех возможных ключей, которые я могу использовать в приложении, например, номер броска для студента, если это 4 цифры, то этот юниверс представляет собой набор чисел от 1 до 9999) и способ сопоставить их с конечным набором чисел, которые я могу выделить в моей системе, теоретически моя хеш-таблица готова.
Как правило, в приложениях размер юниверса ключей очень велик, чем количество элементов, которые я хочу добавить в хэш-таблицу (я не хочу тратить 1 ГБ памяти на хэш, скажем, 10000 или 100000 целочисленных значений, потому что в двоичном представлении они имеют длину 32 бита). Итак, мы используем это хеширование. Это своего рода смешанная «математическая» операция, которая отображает мою большую вселенную на небольшой набор значений, которые я могу разместить в памяти. В практических случаях часто пространство хеш-таблицы имеет тот же «порядок» (big-O), что и (количество элементов * размер каждого элемента), поэтому мы не тратим много памяти.
Теперь, большой набор сопоставлен с небольшим набором, отображение должно быть много-к-одному. Таким образом, разные ключи будут выделены в одном и том же месте (не справедливо). Есть несколько способов справиться с этим, я просто знаю два популярных:
- Используйте пространство, которое должно было быть выделено для значения, как ссылку на связанный список. В этом связанном списке будут храниться одно или несколько значений, которые находятся в одном и том же слоте во многих сопоставлениях. Связанный список также содержит ключи, которые помогут кому-то, кто приходит на поиски. Это как многие люди в одной квартире, когда приходит разносчик, он идет в комнату и специально спрашивает парня.
- Используйте двойную хеш-функцию в массиве, которая каждый раз выдает одну и ту же последовательность значений, а не одно значение. Когда я иду, чтобы сохранить значение, я вижу, свободна ли требуемая область памяти или занята. Если это бесплатно, я могу хранить свое значение там, если оно занято, я беру следующее значение из последовательности и так далее, пока не найду свободное место и не сохраню свое значение там. При поиске или получении значения я возвращаюсь по тому же пути, который указан в последовательности, и в каждом месте запрашиваю vaue, будет ли оно там, пока не найду его или не найду все возможные местоположения в массиве.
Введение в алгоритмы, разработанное CLRS, дает очень хорошее представление о теме.