Доступ по ключу за константу в среднем. Основа словарей и множеств — и структура, на которой держится большая часть повседневного кода.
Ключевые тезисы
- Средняя стоимость операции O(1), худшая — O(n) при массовых коллизиях.
- разрешают цепочками или открытой адресацией; выбор влияет на поведение при заполнении.
- Порядок обхода не гарантирован по смыслу структуры — полагаться на него нельзя.
Какую задачу решает
редко бывает целью задачи — она почти всегда способ убрать вложенный цикл. Вместо того чтобы искать элемент проходом по коллекции, мы вычисляем по ключу адрес и обращаемся к нему напрямую: поиск превращается в узнавание, а квадрат — в линию.
Цена известна заранее и её стоит проговаривать: памяти, отсутствие порядка обхода и худший случай на операцию, который на данных извне может быть организован намеренно. Поэтому разбор идёт и по типовым применениям, и по внутреннему устройству.
Подробный разбор
«Для каждого заказа найти пользователя», «есть ли такой элемент среди уже виденных», «сколько раз встречается значение» — если ответ ищется проходом по коллекции, а сам вопрос задаётся в цикле, получается . Миллион заказов и миллион пользователей — это операций.
Отсортировать и искать бинарно — уже лучше, , но порядок здесь никому не нужен: вопрос-то про точное совпадение ключа, а не про соседей по величине.
Вычислить по ключу адрес ячейки и обратиться к ней напрямую — вместо того чтобы искать. Поиск превращается в узнавание.
Отсюда средняя стоимость операции — константа, не зависящая от размера коллекции. Плата известна заранее: памяти, отсутствие порядка и худший случай, который надо держать в уме.
a: 2 7 11 15 цель 9
2: нужно 9 - 2 = 7 -> семёрку ещё не видели, запоминаем двойку
7: нужно 9 - 7 = 2 -> двойка в таблице есть -> ответ 2 + 7Ключевой ход — перевернуть вопрос. Мы не ищем пару к текущему элементу среди оставшихся, а заранее знаем, какое число нам нужно, и проверяем, встречалось ли оно. Один проход вместо перебора пар.
a b a c b a
a -> 3
b -> 2
c -> 1
теперь любой вопрос про количество -- одно обращениеТаблица — это массив корзин и функция, переводящая ключ в его номер: . Разные ключи могут попасть в одну корзину — это , и её нужно разрешать. Два основных способа: цепочки (в корзине список) и открытая адресация (ищем следующую свободную ячейку по правилу пробирования).
| Операция | Средний случай | Худший случай |
|---|---|---|
| Поиск по ключу | — все ключи в одной корзине | |
| Вставка | амортизированно | при рехешировании |
| Удаление | ||
| Память | плюс запас под коэффициент заполнения |
- Коэффициент заполнения — отношение числа элементов к числу корзин; при превышении порога таблица перестраивается вдвое больше.
- Рехеширование стоит , но случается всё реже — отсюда амортизированная константа на вставку.
- Требование к ключу: неизменяемость и согласованность хеша с равенством. Равные ключи обязаны иметь равный хеш.
Константа получается не из магии, а из двух допущений. Первое: хеш-функция распределяет ключи по корзинам примерно равномерно, поэтому средняя длина цепочки равна коэффициенту заполнения, а он поддерживается ограниченным. Второе: рост таблицы геометрический — при удвоении суммарная стоимость всех рехеширований линейна, то есть на операцию приходится константа.
Оба допущения можно нарушить, и тогда таблица деградирует до списка. Если ключи подобраны злонамеренно под известную хеш-функцию, все они попадут в одну корзину: это не теоретический риск, а класс атак на веб-сервисы (HashDoS). Защита — рандомизация хеша при старте процесса, и в современных языках она включена по умолчанию.
| Сигнал в условии | Что класть в таблицу |
|---|---|
| «Есть ли дубликат», «встречалось ли раньше» | множество увиденных |
| «Сколько раз», «самый частый» | счётчик |
| Пара или тройка с условием, массив не отсортирован | дополнение до цели: значение → индекс |
| «Сгруппировать», «объединить одинаковые по смыслу» | каноническая форма → список |
| «Сколько подотрезков с суммой k» | префиксная сумма → сколько раз встречалась |
| Нужен порядок, диапазон или ближайший по значению | не таблица, а дерево или |
| Ключи — целые в небольшом диапазоне | обычный массив-счётчик: он быстрее |
| Форма | Что хранит | Типичное применение |
|---|---|---|
| Множество | только факт присутствия | дубликаты, пересечения, посещённые состояния |
| Счётчик | ключ → число | частоты, анаграммы, топ-k вместе с кучей |
| Словарь-индекс | ключ → позиция или объект | соединение двух таблиц, кеш по идентификатору |
| Группировка | каноническая форма → список | анаграммы, дедупликация записей |
| Словарь плюс список | ключ → узел списка | LRU-кеш: порядок и доступ по ключу сразу |
| Bloom-фильтр | биты вместо ключей | «точно нет или возможно да» при жёстком лимите памяти |
Параметры реализации, которые видны снаружи: способ разрешения коллизий (цепочки против открытой адресации — вторая быстрее по кешу, но чувствительнее к заполнению), пороговый коэффициент заполнения и рандомизация хеша.
- Изменяемый ключ. Положили список или объект, изменили его — и запись больше не находится: хеш изменился, корзина осталась прежней.
- Хеш не согласован с равенством. Равные объекты с разными хешами дают дубликаты в множестве.
- Опора на порядок обхода. Он не гарантирован по смыслу структуры; нужен порядок — сортируйте явно.
- Память. Таблица держит запас под заполнение: на миллиардах ключей это гигабайты, и иногда дешевле отсортировать.
- Вещественные ключи.
0.1 + 0.2и0.3— разные ключи; для дробных величин используют округление до целых единиц. - Коллизионная атака. Если ключи приходят извне, нужна рандомизация хеша — иначе на операцию по чужому выбору.
| Структура | Когда она лучше | Что теряем |
|---|---|---|
| Дерево поиска | нужны порядок, диапазоны, ближайший по величине | логарифм вместо константы |
| Массив-счётчик | ключи — небольшие целые | не подходит для произвольных ключей |
| Сортировка | данных больше, чем памяти; нужен один проход по группам | , зато предсказуемая память |
| Два указателя | вход уже отсортирован | нужна упорядоченность |
| Bloom-фильтр | память критична, ложные срабатывания допустимы | нельзя хранить значения и удалять |
- Почему средняя стоимость операции — константа, а худшая — ?
- Почему таблица растёт вдвое, а не на фиксированное число корзин?
- Что произойдёт, если положить в множество изменяемый объект и затем его изменить?
- Группировка анаграмм: какой ключ выбрать и почему он одинаков у всех слов группы?
- Когда массив-счётчик предпочтительнее ?
Чем заменяют
Видеолекции
Записи университетских курсов, где эта тема звучит. Где у записи есть тайм-коды, ссылка открывает её с нужной секунды; где их не проставили — с начала.
Связанные темы
Структуры данных
Arrays, Lists, Stacks and Queues85%
Массивы, списки, стеки и очереди · Алгоритмы и структуры данныхЧетыре базовых способа хранить последовательность. Отличаются не тем, что умеют, а тем, что у них дёшево, а что дорого.
Trees and Heaps85%
Деревья и кучи · Алгоритмы и структуры данныхИерархические структуры, дающие логарифмические операции: двоичное дерево поиска для упорядоченных запросов, куча — для быстрого минимума.
Sorting Algorithms85%
Сортировки · Алгоритмы и структуры данныхБыстрая, слиянием, пирамидальная — три способа упорядочить данные за O(n log n) с разными свойствами по памяти, устойчивости и худшему случаю.
Order Statistics85%
Порядковые статистики · Алгоритмы и структуры данныхНайти k-й по величине элемент, не сортируя весь массив. Quickselect делает это в среднем за линию.
Caching85%
Кеширование · Системный дизайн ML-сервисовНе считать то, что уже посчитано: кеш предсказаний, признаков и эмбеддингов.
Всё, что названо в описании, разобрано здесь же или в соседней теме — переходы под каждым определением.
Узнавание вместо поиска
Заранее знаем, какой ключ нужен, и проверяем его наличие за константу.
Разбор ниже: Ключевая идеяКоэффициент заполнения
Отношение числа элементов к числу корзин; порог запускает рехеширование.
Разбор ниже: ФормализацияТребования к ключу
Неизменяемость и согласованность хеша с равенством.
Разбор ниже: Ограничения и типичные ошибкиКоллизионная атака
Подобранные ключи превращают таблицу в список — отсюда рандомизация хеша.
Разбор ниже: Почему это работаетТема встречается в тесте по направлениям — ошибки приведут обратно на эту страницу.
Глава «Алгоритмы и структуры данных» последний раз правилась . Нашли ошибку — напишите.