Почти любая задача с собеседования - это одна из немногих форм в новой обёртке. Каждая страница ниже называет форму, слова в условии, которые на неё указывают, и соседа, с которым её путают.
Массивы
Хеширование
Два указателя
Два указателя (с концов)Two pointers (opposite ends)
O(n)Два индекса стартуют по краям отсортированного массива и идут навстречу. Каждый шаг двигает ту сторону, что мешает ответу.
Два указателя (в одну сторону)Two pointers (same direction)
O(n)Указатель чтения бежит вперёд, а указатель записи идёт следом. Запись сдвигается только там, где значение стоит сохранить.
Скользящее окно
Скользящее окно (фиксированный размер)Sliding window (fixed size)
O(n)Окно ровно из k элементов катится по шагу за раз. Добавьте входящее значение, уберите выходящее, остальное не пересчитывайте.
Скользящее окно (переменное)Sliding window (variable)
O(n)Растите окно справа, а как только условие сломалось, сужайте его слева. Ни один из концов никогда не идёт назад по массиву.
Префиксные суммы
Префиксные суммыPrefix sums
O(n) build / O(1) queryХраните накопленную сумму всего, что стоит до данного индекса. Тогда сумма на отрезке это одно вычитание, а не целый цикл.
Массив разностейDifference array
O(n)Записывайте не значение, а изменение в каждой точке. Обновление отрезка стоит двух записей, а один проход в конце всё собирает.
Поиск
Линейный поискLinear search
O(n)Идите по коллекции с одного конца и остановитесь на первом подходящем элементе. Ни порядка, ни структуры, ни подготовки.
Бинарный поиск (массив)Binary search (array)
O(log n)Посмотрите на середину отсортированного отрезка и выбросьте половину, где ответа быть не может. Повторяйте до одного элемента.
Бинарный поиск во вращённом массивеBinary search on a rotated array
O(log n)Отсортированный массив, разрезанный и переставленный, на каждом шаге хранит одну отсортированную половину. Найдите её и выберите сторону.
Бинарный поиск по ответуBinary search on the answer
O(n log(range))Искать здесь надо не в данных, а в диапазоне возможных ответов. Проверка да или нет говорит, какую половину можно выбросить.
Сортировка
Элементарные сортировки (выбором, пузырьком, вставками)Elementary sorts (selection, bubble, insertion)
O(n^2)Сортировки выбором, пузырьком и вставками сравнивают соседей и меняют их местами. Все три стоят O(n в квадрате) на перемешанных данных.
Быстрая сортировка (merge / quick)Fast sort (merge / quick)
O(n log n)Разрежьте массив надвое, отсортируйте каждую часть и соберите обратно. Merge режет по позиции, а quick режет по значению.
Сортировка без сравнений (подсчётом / поразрядная)Non-comparison sort (counting / radix)
O(n)Сортировки подсчётом и поразрядная не сравнивают значения между собой. Они читают сам ключ и обгоняют n log n на узком диапазоне.
Сортировка с пользовательским компараторомSort with a custom comparator
O(n log n)Компаратор отвечает ровно на один вопрос: какой из двух элементов идёт раньше. Всё остальное берёт на себя сама сортировка.
Интервалы
Интервалы: слияние и вставкаIntervals: merge & insert
O(n log n)Отсортируйте интервалы по началу и пройдите их один раз. Соседи сливаются, если следующее начало не позже текущего конца.
Линия развёртки (подсчёт событий)Sweep line (event counting)
O(n log n)Превратите каждый интервал в событие начала и событие конца. Отсортируйте события по координате и пройдите со счётчиком.
Связные списки
Разворот связного спискаLinked list reversal
O(n)Пройдите список один раз и в каждом узле разверните ссылку на предыдущий узел. Трёх локальных переменных для этого хватит.
Быстрый и медленный указателиFast & slow pointers
O(n)Один указатель делает по одному шагу за раз, а другой сразу по два. Именно разрыв между ними выдаёт циклы и середину списка.
Слияние и перестройка связного спискаLinked list merge & reorder
O(n + m)Стройте ответ на фиктивном узле и берите головы из обоих списков. Фиктивный узел убирает разбор случая пустого результата.
Стеки и очереди
Стек (LIFO)Stack (LIFO)
O(n)Стек всегда отдаёт самый свежий положенный элемент первым. Поэтому он подходит всему, что закрывается в обратном порядке.
Монотонный стекMonotonic stack
O(n)Держите значения в стеке возрастающими снизу вверх. Снимайте всё, что проигрывает новому, и каждое снятие находит ответ.
Монотонная дек-очередь (максимум/минимум окна)Monotonic deque (sliding window max/min)
O(n)Дек, упорядоченный так, что спереди всегда лежит максимум окна. Проигравшие значения уходят сзади, а устаревшие спереди.
Рекурсия
РекурсияRecursion
O(n)Функция вызывает саму себя на уменьшенной версии задачи. Базовый случай её останавливает, а вызовы разворачиваются назад.
Рекурсия с мемоизациейRecursion with memoization
O(states)Рекурсия, которая записывает каждый посчитанный ответ. Когда тот же аргумент приходит снова, отдаётся сохранённое значение.
Перебор с возвратом
Деревья
Обход дереваTree traversal
O(n)Обойдите каждый узел дерева ровно один раз. Именно то, в каком месте вы посещаете узел, и решает, что в итоге вычислит обход.
Двоичное дерево поискаBinary search tree
O(log n) avgДерево поиска держит все меньшие значения слева, а большие справа. Каждый поиск отбрасывает целиком одну сторону дерева.
Наименьший общий предокLowest common ancestor
O(n)Наименьший общий предок это самый глубокий узел, под которым лежат обе цели. Найти его хватает одного обратного обхода дерева.
Сериализация / десериализация дереваTree serialize / deserialize
O(n)Запишите дерево в одну плоскую строку и восстановите его точно таким же. Работает это за счёт меток отсутствующих потомков.
Кучи и топ-k
Графы
Обход графа BFS / DFSGraph BFS / DFS
O(V+E)Обходите граф от стартовой вершины, помечая всё уже увиденное. Очередь даёт кратчайший путь в рёбрах, а стек уходит вглубь.
Компоненты связностиConnected components
O(V+E)Начинайте новый обход в каждой ещё не увиденной вершине. Каждый реально начатый обход означает ещё одну связную группу графа.
Топологическая сортировка (алгоритм Кана)Topological sort (Kahn's algorithm)
O(V+E)Расставьте задачи так, чтобы каждая шла после всех, от которых она зависит. Каждый раз берите вершину, которая никому не должна.
Система непересекающихся множествUnion-find (disjoint set)
near O(1) per opКаждый элемент указывает на родителя, а подъём наверх приводит к корню, который называет группу. Элементы вместе, если корни совпали.
Алгоритм ДейкстрыDijkstra's algorithm
O(E log V)Закрывайте ближайшую незакрытую вершину и ослабляйте все её рёбра. Куча держит эту вершину всегда в одном чтении от вас.
Эйлеров путь (алгоритм Хирхольцера)Eulerian path (Hierholzer's algorithm)
O(E)Пройдите граф, использовав каждое ребро ровно один раз. Кладите вершину в стек, когда она застряла, а ответ читайте с конца.
Проверка двудольности (раскраска в два цвета)Bipartite check (two-coloring)
O(V+E)Покрасьте любую вершину, а потом покрасьте каждого её соседа в другой цвет. Конфликт доказывает, что граф не двудольный.
Жадные алгоритмы
Динамическое программирование
Динамическое программирование (1-D)Dynamic programming (1-D)
O(states)Ответьте на маленькую версию задачи, сохраните ответ и стройте из него следующий. Каждое состояние считается только один раз.
DP над подпоследовательностями (LIS / LCS / расстояние редактирования)DP on subsequences (LIS / LCS / edit distance)
O(n^2)/O(nm)Состояние здесь это пара позиций, по одной в каждой последовательности. Каждая ячейка спрашивает, совпали ли эти элементы.
DP на интервалахDP on intervals
O(n^3)Состояние здесь это отрезок, а переход выбирает последний ход внутри него. Короткие отрезки всегда заполняются раньше длинных.
DP на деревьяхDP on trees
O(n)Ответ каждого узла собирается из ответов его собственных потомков. Один обратный обход по дереву считает их все за проход.
Строки и динамическое программирование
Строки
Битовые операции
Математика и теория чисел
Матрицы
Обход матрицы (спираль / диагонали)Matrix traversal (spiral / diagonal)
O(mn)Обходите сетку в том порядке, которого обычные циклы не дают. Следующую клетку выбирают границы или правило направления.
Поиск в матрице (отсортированная сетка)Matrix search (sorted grid)
O(m+n)В сетке, отсортированной в обе стороны, из угла есть только одно направление. Сравнение отбрасывает целый ряд или столбец.
Поиск слова в матрице (DFS/backtracking по сетке)Matrix word search (grid DFS/backtracking)
O(mn*4^L)Ищите в сетке, шагая в соседа и потом возвращаясь обратно. Клетка помечена ровно столько времени, сколько вы внутри неё.