Menu
Coddy logo textTech

Хеш-таблица

Последнее обновление

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

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

Временная сложность

ОперацияСредний случайХудший случай
ВставкаO(1)O(n) (все ключи сталкиваются)
ПоискO(1)O(n)
УдалениеO(1)O(n)
ПамятьO(n)O(n)

Ключевые понятия

ТерминЗначение
Хеш-функцияОтображает ключ в индекс корзины
КоллизияДва ключа попадают в одну корзину
Метод раздельных цепочекКаждая корзина содержит список сталкивающихся записей
Открытая адресацияАльтернатива: зондировать следующий свободный слот
Коэффициент заполнениязаписи / корзины - определяет изменение размера

Разобранный пример

Вставка ключей 20, 34, 9, 13 в таблицу с 7 корзинами, используя hash(k) = k % 7:

ШагСтруктураДействие
Вставить 20корзина 6: [20]20 % 7 = 6, корзина 6 пуста, сохранить 20
Вставить 34корзина 6: [20, 34]34 % 7 = 6, коллизия с 20, добавить 34 в цепочку
Вставить 9корзина 2: [9]9 % 7 = 2, корзина 2 пуста, сохранить 9
Вставить 13корзина 6: [20, 34, 13]13 % 7 = 6, коллизия, добавить 13 в цепочку корзины 6
Найти 34корзина 6: [20, 34, 13]34 % 7 = 6, просмотр цепочки: 20 нет, 34 совпадает - найдено

Когда использовать хеш-таблицу

Используйте, когдаИзбегайте, когда
Вам нужны вставка, поиск и удаление по ключу в среднем за O(1)Вам нужно хранить ключи в отсортированном порядке - используйте сбалансированное BST
У ключей нет полезного порядка, и вы проверяете только принадлежность по точному совпадениюВам нужны запросы по диапазону или поиск ближайшего ключа
Вы можете позволить себе немного дополнительной памяти для корзин и низкий коэффициент заполненияПамять крайне ограничена, и важен каждый байт
Для вашего типа ключа существует хорошая хеш-функцияЗадержка в худшем случае должна быть ограничена - коллизии делают её O(n)

Код Hash Table

Чистая, готовая к запуску реализация Hash Table на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Hash Table на Python

Python
1class HashTable:2    def __init__(self, size=8):3        self.size = size4        self.buckets = [[] for _ in range(size)]5
6    def _index(self, key):7        # Hash the key to a bucket; different keys can collide8        return sum(ord(ch) for ch in key) % self.size9
10    def set(self, key, value):11        bucket = self.buckets[self._index(key)]12        for i, (k, _) in enumerate(bucket):13            if k == key:14                bucket[i] = (key, value)  # update existing key15                return16        bucket.append((key, value))  # chain on collision17
18    def get(self, key):19        for k, v in self.buckets[self._index(key)]:20            if k == key:21                return v22        raise KeyError(key)23
24    def delete(self, key):25        bucket = self.buckets[self._index(key)]26        for i, (k, _) in enumerate(bucket):27            if k == key:28                del bucket[i]29                return30        raise KeyError(key)31
32
33table = HashTable()34table.set("apple", 3)35table.set("banana", 7)36table.set("cherry", 5)37
38print("apple  ->", table.get("apple"))39print("banana ->", table.get("banana"))40table.set("apple", 10)41print("apple  ->", table.get("apple"))42table.delete("banana")43print("bucket sizes:", [len(b) for b in table.buckets])
Запустите этот код в плейграунде Python

Частые вопросы о хеш-таблице

Что такое хеш-коллизия и как она разрешается?
Коллизия происходит, когда два разных ключа попадают в одну корзину. Два распространённых решения - это метод раздельных цепочек (каждая корзина содержит список, и сталкивающиеся ключи добавляются - показано здесь) и открытая адресация (зондирование следующего пустого слота в массиве). Оба сохраняют корректность поиска; они различаются размещением в памяти и производительностью при высокой нагрузке.
Какова временная сложность хеш-таблицы?
Вставка, поиск и удаление в среднем выполняются за O(1), когда хеш-функция равномерно распределяет ключи и коэффициент заполнения остаётся низким. В худшем случае - когда все ключи сталкиваются в одной корзине - они деградируют до O(n), поэтому важны хорошая хеш-функция и изменение размера.
Что такое коэффициент заполнения?
Коэффициент заполнения - это число хранимых записей, делённое на число корзин. По мере его роста цепочки удлиняются, а операции замедляются, поэтому большинство хеш-таблиц изменяют размер (перехешируют в больший массив), как только он превышает порог вроде 0.75.
Когда следует использовать хеш-таблицу вместо двоичного дерева поиска?
Используйте хеш-таблицу, когда вам нужен только поиск по точному совпадению и вы хотите среднюю скорость O(1) без требования упорядоченности. Используйте сбалансированное двоичное дерево поиска, когда вам нужны отсортированные ключи, запросы по диапазону или поиск предшественника/преемника, чего хеш-таблица не может делать эффективно. BST даёт гарантированные операции O(log n), тогда как хеш-таблица меняет эту гарантию на более быструю среднюю производительность.
В чём разница между методом раздельных цепочек и открытой адресацией?
Метод раздельных цепочек хранит сталкивающиеся ключи в списке для каждой корзины, поэтому корзина может содержать много записей, а таблица никогда по-настоящему не заполняется. Открытая адресация хранит всё в самом массиве и при коллизии зондирует следующий свободный слот, что дружественно к кешу, но резко деградирует, когда коэффициент заполнения приближается к 1, и требует аккуратной обработки удаления. Цепочки допускают более высокие коэффициенты заполнения; открытая адресация использует память более компактно.
Почему нельзя полагаться на то, что хеш-таблица сохранит мои ключи в порядке вставки или сортировки?
Хеш-функция намеренно разбрасывает ключи по корзинам, чтобы избежать кластеризации, поэтому порядок обхода отражает расположение корзин, а не порядок вставки или сортировки. Если вам нужен порядок, используйте упорядоченную карту/дерево или структуру вроде карты с сохранением порядка вставки (например, dict в Python сохраняет порядок вставки, но это гарантия языка, а не неотъемлемое свойство хеш-таблицы). Никогда не предполагайте, что порядок обхода совпадает с тем, как вы вставляли ключи, если только язык явно этого не обещает.
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ