Признак в условии
- «найти пару / дополнение до суммы»
- «есть ли повтор», «первый уникальный»
- «сгруппировать по признаку», «посчитать частоты»
- вложенный цикл, в котором внутренний ищет то, что уже видели
Идея
Меняем время на память: вместо поиска по массиву за O(n) кладём увиденное в Map или Set и проверяем за O(1). Почти всегда превращает O(n²) в O(n).
Шаблон
const seen = new Map();
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
if (seen.has(need)) return [seen.get(need), i];
seen.set(nums[i], i);
}
Сложность
Время O(n), память O(n).
Частые ошибки
- объект вместо Map там, где ключи не строки: 1 и '1' схлопнутся
- запись в словарь до проверки - элемент найдёт сам себя
- проверка
if (map.get(k))вместоmap.has(k)ломается на значении 0
Мнемоника
«Сначала спроси, потом запиши»
Не путать с
- Два указателя - если вход отсортирован, пара ищется указателями за O(1) памяти - хеш-таблица тут избыточна
- Префиксные суммы - когда ищется не элемент, а участок с заданной суммой, одной таблицы мало - нужны префиксы
- Битовые операции - для набора из малого числа флагов битовая маска дешевле Set и быстрее
Задачи на этот паттерн
- Two Sum
- первый неповторяющийся символ
- группировка анаграмм
- есть ли дубликаты
- изоморфные строки
- слова по шаблону
- самая длинная цепочка подряд
- K самых частых слов
- все начала анаграмм