Обзор простой хеш-таблицы
В качестве обновления хеш-таблица - это способ хранения значения под определенным ключом в структуре данных. Например, я могу сохранить значение "a"
под ключом 1
, а затем получить его, просмотрев ключ1
в хэш-таблице.
Простейший пример хеш-таблицы, который я могу придумать, - это хеш-таблица, которая может хранить только целые числа, где ключом для записи хеш-таблицы является также сохраняемое значение. Допустим, ваша таблица имеет размер 8, и это в основном массив в памяти:
---------------------------------
| | | | | | | | |
---------------------------------
0 1 2 3 4 5 6 7
Хэш-функция
Хэш-функции дают вам указатель того, где хранить ваши значения. Довольно простой хеш-функцией для этой таблицы было бы добавить 1 к значению, которое вы хотите сохранить, а затем изменить его на 8 (размер таблицы). Другими словами, ваша хеш-функция(n+1)%8
, где n
есть целое число вы хотите сохранить.
Вставки
Если вы хотите вставить значение в эту хеш-таблицу, вы вызываете свою хеш-функцию (в данном случае (n+1)%8
) для значения, которое вы хотите вставить, чтобы получить индекс. Например, если мы хотим вставить 14, мы бы вызвали (14 + 1) % 8
и получили индекс 7
, поэтому мы вставили бы его значение в индекс 7
.
---------------------------------
| | | | | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Аналогично, мы можем вставить 33, 82 и 191 так:
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Столкновения
Но что произойдет, если мы попытаемся вставить что-то, что столкнется с записью? 2 должно идти в индексе3
, но оно занято 82. Есть несколько способов решить эту проблему, самый простой - снова и снова вызывать нашу хэш-функцию, пока мы не найдем пустое место.
Итак, логика такова:
- (2 + 1)% 8 = 3
- Индекс 3 полон
- Подключите 3 обратно в нашу хэш-функцию. ( 3 + 1)% 8 = 4 , что пусто.
- Поместите наше значение в индекс 4 .
Теперь хеш-таблица выглядит следующим образом со значением 2, хранящимся в индексе 4
.
---------------------------------
|191| |33 |82 |2 | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Недостатком этого решения является то, что довольно скоро наш стол заполнится! Если вы знаете, что размер ваших данных ограничен, это не должно вызывать проблем, если ваша таблица достаточно велика, чтобы вместить все возможные значения. Если вы хотите иметь возможность удерживать больше, вы можете обрабатывать столкновения по-разному. Давайте вернемся туда, где мы были до вставки 2.
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Если вы помните, (2+1)%8
дает нам индекс 3
, который взят. Если вы не хотите, чтобы ваша хеш-таблица заполнялась, вы можете использовать каждый индекс таблицы как связанный список и добавлять к списку этот индекс. Поэтому вместо повторного вызова хеш-функции мы просто добавим в список индекс 3
:
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Этот список может увеличиться настолько, насколько позволит память. Я могу вставить 18, и он будет просто добавлен к 2:
-----
|18 |
-----
| 2 |
---------------------------------
|191| |33 |82 | | | |14 |
---------------------------------
0 1 2 3 4 5 6 7
Lookups
Поиск значений в вашей хеш-таблице выполняется быстро, учитывая, что ваша хеш-таблица имеет довольно большой размер. Вы просто вызываете свою хэш-функцию и получаете индекс. Допустим, вы хотите увидеть, есть ли 82 в вашей таблице. Функция поиска вызовет (82+1)%8
= 3
, и посмотрит на элемент в индексе 3
, и вернет его для вас. Если вы посмотрели 16, функция поиска будет выглядеть в индексе1
и увидела, что она не существует.
Поиски тоже должны обрабатывать коллизии!
Если вы попытаетесь найти значение 2, ваша хеш-таблица должна будет использовать ту же логику коллизий, которая использовалась для хранения данных, так и для извлечения данных. В зависимости от того, как работает ваша хеш-таблица, вы либо хешируете ключ снова и снова, пока не найдете нужную запись (или не найдете пустое место), либо перебираете связанный список, пока не найдете элемент (или добрался до конца списка)
Резюме
Таким образом, хеш-таблицы - это хороший способ быстрого хранения и доступа к парам ключ-значение. В этом примере мы использовали тот же ключ, что и значение, но в реальных хеш-таблицах ключи не так ограничены. Хеш-функции будут работать с ключами для генерации индекса, а затем ключ / значение могут быть сохранены в этом индексе. Хеш-таблицы на самом деле не предназначены для итерации, хотя это возможно. Как видите, в хеш-таблицах может быть много пустых пространств, и итерация по ним будет пустой тратой времени. Даже если хеш-таблица имеет логику для пропуска поиска пустого пространства в своем итераторе, вам лучше использовать структуру данных, предназначенную для итераторов, например, связанные списки.