Перейти к содержанию

Хеш-таблица

Материал из Википедии — свободной энциклопедии
Hash table
Тип ассоциативный массив
Год изобретения 1953
Сложность в О-символике
В среднем В худшем случае
Расход памяти
Поиск
Вставка
Удаление
Логотип Викисклада Медиафайлы на Викискладе

Хеш-табли́ца — структура данных, реализующая интерфейс ассоциативного массива, а именно, она позволяет хранить пары (ключ, значение) и выполнять три операции: операцию добавления новой пары, операцию удаления и операцию поиска пары по ключу.

Существуют два основных варианта хеш-таблиц: с открытой адресацией и списками. Хеш-таблица является массивом , элементы которого есть пары (хеш-таблица с открытой адресацией) или списки пар (хеш-таблица со списками).

Выполнение операции в хеш-таблице начинается с вычисления хеш-функции от ключа. Получающееся хеш-значение играет роль индекса в массиве . Затем выполняемая операция (добавление, удаление или поиск) перенаправляется объекту, который хранится в соответствующей ячейке массива .

Ситуация, когда для различных ключей получается одно и то же хеш-значение, называется коллизией. Такие события не так уж и редки — например, при вставке в хеш-таблицу размером 365 ячеек всего лишь 23 элементов вероятность коллизии уже превысит 50% (если каждый элемент может равновероятно попасть в любую ячейку) — см. парадокс дней рождения. Поэтому механизм разрешения коллизий — важная составляющая любой хеш-таблицы.

В некоторых специальных случаях удаётся избежать коллизий вообще. Например, если все ключи элементов известны заранее (или очень редко меняются), то для них можно найти некоторую совершенную хеш-функцию, которая распределит их по ячейкам хеш-таблицы без коллизий. Хеш-таблицы, использующие подобные хеш-функции, не нуждаются в механизме разрешения коллизий, и называются хеш-таблицами с прямой адресацией.

Число хранимых элементов, делённое на размер массива (число возможных значений хеш-функции), называется коэффициентом заполнения хеш-таблицы (load factor) и является важным параметром, от которого зависит среднее время выполнения операций.

Свойства хеш-таблицы

[править | править код]

Важное свойство хеш-таблиц состоит в том, что при некоторых разумных допущениях все три операции (добавление, удаление, поиск элемента) в среднем выполняются за время . Но при этом не гарантируется, что время выполнения отдельной операции мало́. Это связано с тем, что при достижении некоторого значения коэффициента заполнения необходимо осуществлять перестройку индекса хеш-таблицы: создать новый массив увеличенного размера и заново добавить в него все пары из старого массива пар (он же старая хеш-таблица).

Разрешение коллизий

[править | править код]

Существует несколько способов разрешения коллизий.

Метод списков

[править | править код]
Разрешение коллизий при помощи цепочек.

Каждая ячейка массива является указателем на связный список пар ключ-значение, соответствующих одному и тому же хеш-значению ключа. Коллизии просто приводят к тому, что появляются списки длиной более одного элемента.

Операции поиска или удаления элемента требуют просмотра всех элементов соответствующего ему списка, чтобы найти в нём элемент с заданным ключом. Для добавления элемента нужно добавить его в конец или начало соответствующего списка и в случае, если коэффициент заполнения станет слишком велик, увеличить размер массива и перестроить таблицу.

При предположении, что каждый элемент может попасть в любую позицию таблицы с равной вероятностью и независимо от того, куда попал любой другой элемент, среднее время работы операции поиска элемента составляет , где  — коэффициент заполнения таблицы.

Открытая адресация

[править | править код]
Пример хеш-таблицы с открытой адресацией и линейным пробированием, получающейся при вставке элементов в левой колонке сверху вниз.

В массиве хранятся сами пары ключ-значение. Алгоритм вставки элемента проверяет ячейки массива в некотором порядке до тех пор, пока не будет найдена первая свободная ячейка, в которую и будет записан новый элемент. Этот порядок вычисляется на лету, что позволяет сэкономить на памяти для указателей, требующихся в хеш-таблицах с цепочками.

Последовательность, в которой просматриваются ячейки хеш-таблицы, называется последовательностью проб. В общем случае, она зависит только от ключа элемента, то есть это последовательность вида , , …, , где  — ключ элемента, а  — произвольные функции, сопоставляющие каждому ключу ячейку в хеш-таблице. Первый элемент в последовательности, как правило, равен значению некоторой хеш-функции от ключа, а остальные считаются от него одним из приведённых ниже способов. Для успешной работы алгоритмов поиска последовательность проб должна быть такой, чтобы все ячейки хеш-таблицы оказались просмотренными ровно по одному разу.

Алгоритм поиска просматривает ячейки хеш-таблицы в том же самом порядке, что и при вставке, до тех пор, пока не найдётся либо элемент с искомым ключом, либо свободная ячейка (что означает отсутствие элемента в хеш-таблице).

Удаление элементов в такой схеме несколько затруднено. Обычно поступают так: заводят булевый флаг для каждой ячейки, помечающий, удалён элемент в ней или нет. Тогда удаление элемента состоит в установке этого флага для соответствующей ячейки хеш-таблицы, но при этом необходимо модифицировать процедуру поиска существующего элемента так, чтобы она считала удалённые ячейки занятыми, а процедуру добавления — чтобы она их считала, наоборот, свободными и сбрасывала значение флага при добавлении.

Последовательности проб

Ниже приведены некоторые распространённые типы последовательностей проб. Сразу оговорим, что нумерация элементов последовательности проб и ячеек хеш-таблицы ведётся от нуля, а  — размер хеш-таблицы (и, как замечено выше, также и длина последовательности проб).

  • Линейное пробирование: ячейки хеш-таблицы последовательно просматриваются с некоторым фиксированным интервалом между ячейками (обычно ), то есть -й элемент последовательности проб — это ячейка с номером . Для того, чтобы все ячейки оказались просмотренными по одному разу, необходимо, чтобы было взаимно простым с размером хеш-таблицы.
  • Квадратичное пробирование: интервал между ячейками с каждым шагом увеличивается на константу. Если размер хеш-таблицы равен степени двойки (т. е. ), то одним из примеров последовательности, при которой каждый элемент будет просмотрен по одному разу, является:
    , , , , … — треугольные числа.
  • Двойное хеширование: интервал между ячейками фиксирован, как при линейном пробировании, но, в отличие от него, размер интервала вычисляется второй, вспомогательной хеш-функцией, а значит, может быть различным для разных ключей. Значения этой хеш-функции должны быть ненулевыми и взаимно простыми с размером хеш-таблицы, что проще всего достичь, взяв простое число в качестве размера, и потребовав, чтобы вспомогательная хеш-функция принимала значения от до .

Ниже представлен код, демонстрирующий двойное хеширование:

Литература

[править | править код]
  • Кормен, Т., Лейзерсон, Ч., Ривест, Р., Штайн, К. Глава 11. Хеш-таблицы. // Алгоритмы: построение и анализ = Introduction to Algorithms / Под ред. И. В. Красикова. — 2-е изд. М.: Вильямс, 2005. — 1296 с. ISBN 5-8459-0857-4.