--- title: Hash Tables and Hashing Functions localeTitle: Хэш-таблицы и функции хеширования --- ### Введение в хеширование Хеширование предназначено для решения проблемы необходимости эффективного поиска или хранения элемента в коллекции. Например, если у нас есть список из 10 000 слов английского языка, и мы хотим проверить, указано ли данное слово в списке, было бы неэффективно последовательно сравнивать слово со всеми 10 000 элементами, пока мы не найдем совпадение. Даже если список слов лексикографически отсортирован, как в словаре, вам все равно потребуется некоторое время, чтобы найти искомое слово. Хеширование - это метод, позволяющий сделать вещи более эффективными, эффективно сужая поиск с самого начала. ## Что такое хеширование? Хеширование означает использование некоторой функции или алгоритма для сопоставления данных объекта с некоторым репрезентативным целым значением. Этот так называемый хеш-код (или просто хеш) можно затем использовать как способ сузить наш поиск при поиске элемента на карте. Как правило, эти хеш-коды используются для создания индекса, в котором значение сохраняется. ## Как работает хэш В хэш-таблицах вы храните данные в виде пар ключей и значений. Ключ, который используется для идентификации данных, задается как вход для функции хэширования. Хэш-код, который является целым числом, затем отображается на фиксированный размер, который у нас есть. Хэш-таблицы должны поддерживать 3 функции. * insert (ключ, значение) * get (ключ) * удалить (ключ) В качестве примера, чтобы помочь нам понять концепцию, предположим, что мы хотим сопоставить список строковых ключей со строковыми значениями (например, сопоставить список стран со своими столичными городами). Итак, скажем, мы хотим сохранить данные в таблице на карте. Ключ | Стоимость \---------------- | ------------- Куба | Гавана Англия | Лондон Франция | Париж Испания | Мадрид Швейцария | Берн Предположим, что наша хеш-функция состоит в том, чтобы просто взять длину строки. Для простоты мы будем иметь два массива: один для наших ключей и один для значений. Поэтому, чтобы поместить элемент в хэш-таблицу, мы вычисляем его хеш-код (в этом случае просто подсчитываем количество символов), затем кладем ключ и значение в массивы по соответствующему индексу. Например, у Кубы есть хеш-код (длина) 4. Таким образом, мы сохраняем Кубу в 4-й позиции в массиве ключей, а Гавана - в 4-м индексе массива значений и т. Д. И мы получаем следующее: Должность | Ключ-массив | Массив значений \--------------------- | ------------------ | --------- ------ 1 | | 2 | | 3 | | 4 | Куба | Гавана 5 | Испания | Мадрид 6 | Франция | Париж 7 | Англия | Лондон 8 | | 9 | | 10 | | 11 | Швейцария | Берн Теперь в этом конкретном примере все работает очень хорошо. Наш массив должен быть достаточно большим для размещения самой длинной строки, но в этом случае это всего лишь 11 слотов. Мы немного теряем пространство, потому что, например, в наших данных нет 1-буквенных ключей, а также ключей от 8 до 10 букв. Но в этом случае потерянное пространство тоже не так плохо. Взятие длины строки является приятным и быстрым, а также процесс нахождения значения, связанного с заданным ключом (конечно, быстрее, чем до пяти сопоставлений строк). Но что нам делать, если наш набор данных имеет строку, которая имеет более 11 символов? Что, если у нас есть еще одно слово с 5 символами «Индия» и попробуйте присвоить его индексу, используя нашу хэш-функцию. Поскольку индекс 5 уже занят, нам нужно сделать вызов, что с ним делать. Это называется столкновением. Если в нашем наборе данных была строка с тысячами символов, и вы создадите массив тысяч индексов для хранения данных, это приведет к потерям пространства. Если бы наши ключи были случайными словами с английского языка, где столько слов с одинаковой длиной, использование длины как функции хэширования было бы бесполезным. ## Обработка столкновений Для обработки столкновений используются два основных метода. 1. Раздельная цепочка 2. Открытая адресация #### Раздельная цепочка Обработка столкновений хэшей путем отдельной цепочки использует дополнительную структуру данных, предпочтительно связанный список для динамического распределения, в ведра. В нашем примере, когда мы добавляем Индию в набор данных, она добавляется к связанному списку, хранящемуся в индексе 5, тогда наша таблица будет выглядеть так. Должность | Связанные списки глав | \--------------------- | ---------------------------- -------- | 1 | | 2 | | 3 | | 4 | [Куба-Гавана\] | 5 | \[Испания-Мадрид\] -> \[Индия-Дели\] | 6 | \[Франция-Париж\] | 7 | \[Англия-Лондон\] | 8 | | 9 | | 10 | | 11 | \[Швейцария-Берн\] |](https://en.wikipedia.org/wiki/Linear_probing) Чтобы найти предмет, сначала переходим к ведру, а затем сравниваем ключи. Это популярный метод, и если используется список ссылок, хэш никогда не заполняется. Стоимость `get(k)` в среднем равна `O(n)` где n - количество ключей в ковше, общее количество ключей - N. Проблема с отдельной цепочкой заключается в том, что структура данных может расти без ограничений. #### Открытая адресация Открытая адресация не вводит никакой новой структуры данных. Если происходит столкновение, мы ищем доступность в следующем месте, генерируемом алгоритмом. Open Addressing обычно используется, когда пространство для хранения является ограниченным, то есть встроенным процессором. Открытая адресация не обязательно быстрее, чем отдельная цепочка. Методы открытой адресации * \[Линейное зондирование * [Квадратичное зондирование](https://en.wikipedia.org/wiki/Quadratic_probing) * [Двойной хеши](https://en.wikipedia.org/wiki/Double_hashing) ## Как использовать хэширование в вашем коде. #### питон ``` # Few languages like Python, Ruby come with an in-built hashing support. # Declaration my_hash_table = {} my_hash_table = dict() # Insertion my_hash_table[key] = value # Look up value = my_hash_table.get(key) # returns None if the key is not present || Deferred in python 3, available in python 2 value = my_hash_table[key] # throws a ValueError exception if the key is not present # Deletion del my_hash_table[key] # throws a ValueError exception if the key is not present # Getting all keys and values stored in the dictionary keys = my_hash_table.keys() values = my_hash_table.values() ``` ![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ": Ракета:") [Код запуска](https://repl.it/CVtK) #### Джава ``` // Java doesn't include hashing by default, you have to import it from java.util library // Importing hashmaps import java.util.HashMap; // Declaration HashMap myHashTable = new HashMap(); // declares an empty map. // Insertion myHashTable.put(key, value); // Deletion myHashtable.remove(key); // Look up myHashTable.get(key); // returns null if the key K is not present myHashTable.containsKey(key); // returns a boolean value, indicating the presence of a key // Number of key, value pairs in the hash table myHashTable.size(); ``` ![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ": Ракета:") [Код запуска](https://repl.it/CVt1) ## Ресурсы * Для дальнейшего чтения вы хотите попробовать эту [ссылку](http://geeksquiz.com/hashing-set-1-introduction/) , которая объясняет хеширование с использованием другого примера. * [Хеширование через 60 секунд](https://www.youtube.com/watch?v=x05KubVlh_M) . * [Хвост кукушки](https://www.youtube.com/watch?v=HRzg0SzFLQQ) * [Consisten Hashing](https://www.youtube.com/watch?v=jznJKL0CrxM) * [Цветные фильтры](https://www.youtube.com/watch?v=-SuTGoFYjZs) * [Стратегии хеширования](https://www.youtube.com/watch?v=D65JQ0qQwZk) * [Хеширование паролей](https://crackstation.net/hashing-security.htm) * [Разница между Хешированием и шифрованием](http://stackoverflow.com/questions/326699/difference-between-hashing-a-password-and-encrypting-it)