Банк задач для собеседований по программированию
Частые задачи по программированию, которые снова и снова встречаются на собеседованиях разработчиков. Для каждой: какой паттерн распознать, подход простыми словами и сложность, которую нужно назвать. Интервьюеры оценивают рассуждения, которые вы проговариваете, а не только итоговый код, — тренируйтесь объяснять подход вслух.
Two Sum II (отсортированный массив)
ЛёгкаяПоставьте указатели на оба конца отсортированного массива. Если сумма слишком мала, сдвигайте левый указатель вправо; если слишком велика — правый влево. Отсортированность гарантирует, что вы не пропустите подходящую пару, — именно этот инвариант интервьюеры хотят услышать вслух.
Valid Anagram
ЛёгкаяПосчитайте частоты символов первой строки, уменьшайте их при проходе по второй и проверьте, что все счётчики вернулись к нулю. Упомяните продолжение раньше, чем вас спросят: для полного Unicode массив на 26 ячеек уже не подходит — используйте хеш-таблицу.
Linked List Cycle
ЛёгкаяСдвигайте один указатель на один узел, а другой — на два; если они когда-нибудь встретятся, цикл есть. Будьте готовы к классическому продолжению — найти вход в цикл: верните один указатель в начало списка и двигайте оба по одному узлу, пока они снова не встретятся.
Majority Element
ЛёгкаяХраните кандидата и счётчик: при совпадении увеличивайте, при несовпадении уменьшайте, а когда счётчик дойдёт до нуля, меняйте кандидата. Поскольку элемент большинства встречается больше n/2 раз, он всегда выживает. Объяснить, ПОЧЕМУ он выживает, — в этом и суть собеседования.
Best Time to Buy and Sell Stock
ЛёгкаяОтслеживайте минимальную цену на данный момент и лучшую прибыль, если продать сегодня. Один проход, две переменные. Это простейший пример идеи «нести лучшее состояние префикса», которая позже появится в алгоритме Кадане, — если назовёте эту связь, получите дополнительные очки.
Merge Intervals
СредняяОтсортируйте интервалы по началу, затем пройдите по ним: если текущий интервал начинается после конца последнего объединённого, добавьте его; иначе продлите конец объединённого до максимума из двух. Основную работу делает сортировка — скажите об этом и правильно обработайте граничные случаи компаратора (соприкасающиеся интервалы).
Longest Consecutive Sequence
СредняяПоложите все числа во множество; начинайте счёт только с тех чисел, у которых нет предшественника (начала последовательностей), и идите вперёд. Каждый элемент посещается не более двух раз — так вы защитите оценку O(n) от возражения «но здесь же вложенный цикл».
Product of Array Except Self
СредняяДва прохода без деления: сначала запишите в каждую ячейку произведение всех элементов слева от неё, затем пройдите справа налево, домножая на произведение всех элементов справа. Решения с делением ломаются на нулях — интервьюеры обычно прямо их запрещают.
Min Stack
СредняяРядом со стеком значений храните стек минимумов, вершина которого всегда равна минимуму всего, что под ней: кладите min(новое значение, текущая вершина), извлекайте из обоих стеков синхронно. Это вопрос на проектирование: оценивают инвариант, а не объём кода.
LRU Cache
СредняяХеш-таблица даёт доступ за O(1) к узлам двусвязного списка, упорядоченного по давности использования; при обращении перемещайте узел в голову, при переполнении вытесняйте из хвоста. Фиктивные головной и хвостовой узлы убирают все граничные случаи с проверками на null — упомяните их до того, как начнёте писать код.
Number of Islands
СредняяПройдите по сетке; каждая непосещённая клетка суши запускает заливку (DFS или BFS), которая помечает весь остров посещённым, а вы считаете запуски. Назовите стратегию пометки посещённых клеток («затопление» на месте или отдельное множество) и риск переполнения стека рекурсии на огромных сетках — это сигнал сеньорности.
Course Schedule
СредняяПредставьте пререквизиты как ориентированный граф; вопрос «можно ли пройти все курсы» — это ровно вопрос «ацикличен ли граф». Подойдёт и алгоритм Кана (раз за разом удаляйте вершины с нулевой входящей степенью), и DFS с тремя цветами — выберите один и объясните, почему оставшаяся вершина означает цикл.
Binary Tree Level Order Traversal
СредняяBFS с очередью, но в начале каждого раунда запоминайте длину очереди, чтобы выдавать по одному списку на уровень. Этот приём со снимком длины — переиспользуемое ядро: зигзагообразный обход и вид справа — тот же цикл с другим шагом сбора.
Validate Binary Search Tree
СредняяРекурсия с допустимым окном (min, max), которое сужается на каждом шаге, — или симметричный обход с проверкой, что последовательность строго возрастает. Классическая ловушка — сравнивать детей только с их родителем; постройте контрпример сами, раньше интервьюера.
Word Pattern
ЛёгкаяОтображайте символы шаблона в слова И слова обратно в символы — одно направление пропустит шаблон «ab» для «dog dog». Проверка биекции в обе стороны — весь фокус; произнесите слово «биекция» и обработайте несовпадение длины до того, как начнёте писать код.
Happy Number
ЛёгкаяЕсли раз за разом заменять число суммой квадратов его цифр, вы либо дойдёте до 1, либо попадёте в цикл, — значит, это замаскированная Linked List Cycle. Найдите цикл с помощью множества уже встреченных значений или впечатлите медленным и быстрым указателями Флойда — O(1) по памяти. Назвать сведение к поиску цикла — ход сеньора.
Gas Station
СредняяЕсли суммарный бензин ≥ суммарного расхода, ответ существует и он единственный. Пройдите один раз, отслеживая текущий запас в баке; как только он становится отрицательным, ни одна заправка на провалившемся отрезке не может быть стартовой — начинайте заново со следующей. Собеседование — это обоснование этого пропуска, а не сам цикл.
Jump Game II
СредняяСчитайте индексы, достижимые за k прыжков, слоем BFS: отслеживайте правую границу текущего слоя и самую дальнюю достижимую точку; когда проходите за границу, увеличивайте число прыжков и сдвигайте границу к этой самой дальней точке. Если подать решение как BFS без очереди, становится понятно, ПОЧЕМУ жадный подход здесь оптимален.
Insert Interval
СредняяВыведите интервалы, которые заканчиваются до начала нового, затем поглотите новым все пересекающиеся с ним интервалы (минимальное начало, максимальный конец), затем выведите остальные. Отсортированный вход означает один проход без повторной сортировки — если спросят, почему это проще, сравните с Merge Intervals.
Rotate Image
СредняяПоворот на 90° по часовой стрелке = транспонирование, затем разворот каждой строки. Два чистых прохода с O(1) дополнительной памяти лучше, чем выводить под давлением циклическую перестановку четырёх элементов, — но будьте готовы объяснить отображение координат (i,j) → (j, n−1−i), если интервьюер копнёт глубже этого приёма.
Set Matrix Zeroes
СредняяИспользуйте первую строку и первый столбец как хранилище флагов того, какие строки и столбцы нужно обнулить, а два булевых флага запомнят их собственное состояние. Пройдите вслух по лестнице памяти — O(mn) на копию → O(m+n) на множества → O(1) на заимствованное хранилище, — потому что проверяют именно эту лестницу.
H-Index
СредняяОтсортируйте по убыванию и найдите наибольшее i, при котором citations[i] ≥ i+1, — или обойдитесь без сортировки с помощью корзин подсчёта, ограниченных n, за O(n). Точно сформулируйте определение до того, как начнёте писать код: большинство провалов на этой задаче — от неверного прочтения «h статей, у каждой из которых не меньше h цитирований», а не от алгоритма.
Course Schedule II
СредняяТот же граф, что в Course Schedule, но теперь алгоритм Кана оправдывает себя: порядок, в котором вершины с нулевой входящей степенью покидают очередь, И ЕСТЬ допустимый порядок курсов. Если выведенный порядок короче числа курсов, есть цикл — верните пустой результат. Упомяните альтернативу — обратный порядок завершения вершин в DFS.
Minimum Window Substring
СложнаяРасширяйте правую границу, пока окно не покроет все нужные символы (ведите счётчик «собрано / требуется», а не сравнивайте целиком таблицы на каждом шаге), затем сжимайте левую границу до минимума, пока окно остаётся допустимым, запоминая лучшее. Именно оптимизация со счётчиком сохраняет O(n) — объясните её явно.
Trapping Rain Water
СложнаяВода над каждым столбиком = min(max-left, max-right) − height. Два указателя, движущиеся внутрь с обоих концов, позволяют разрешить ту сторону, где текущий максимум меньше, потому что граница этой стороны уже окончательна. Объясните, почему эта уверенность оправдана, — в этом весь вопрос.