Алгоритмические задачи на собеседовании — это короткие задачи на массивы, строки, хеш-таблицы, деревья и графы, которые нужно решить за 20–40 минут с оценкой сложности. Почти все они сводятся к 12–15 паттернам: два указателя, скользящее окно, префиксные суммы, бинарный поиск, стек, BFS и DFS, куча, динамическое программирование. Готовиться эффективнее по паттернам: 100–150 задач уровня Easy и Medium на LeetCode, решённых с разбором, плюс 5–10 пробных интервью вслух обычно хватает для секции в крупной компании.
Что такое алгоритмическая секция и кому её проходить
Алгоритмическая секция — этап технического собеседования, где кандидат решает одну-две задачи в онлайн-редакторе на выбранном языке. Её проводят многие крупные технологические компании и в России, и за рубежом, причём не только для бэкендеров: фронтендеры, мобильные разработчики, ML-инженеры и аналитики тоже нередко её проходят. Небольшие продуктовые и аутсорс-компании чаще заменяют алгоритмы практической задачей или тестовым заданием.
Что оценивает интервьюер:
- Понимание задачи. Уточнили ли вы ограничения и граничные случаи.
- Выбор подхода. Предложили ли наивное решение и как перешли к оптимальному.
- Сложность. Можете ли оценить время и память в нотации O-большое.
- Код. Работает ли он, читается ли, обрабатывает ли краевые случаи.
- Коммуникацию. Рассуждаете ли вслух и используете ли подсказки.
O-большое (Big O) — способ описать, как растут время работы или память при увеличении входных данных. O(n) значит «пропорционально размеру входа», O(n²) — «пропорционально квадрату».
| Сложность | Пример | Когда допустима при n = 10⁵ |
|---|---|---|
| O(1) | Доступ по индексу, поиск в хеш-таблице | Всегда |
| O(log n) | Бинарный поиск | Всегда |
| O(n) | Один проход по массиву | Да |
| O(n log n) | Сортировка | Да |
| O(n²) | Перебор всех пар | Нет, уже 10¹⁰ операций |
| O(2ⁿ) | Перебор всех подмножеств | Только при n до 20–25 |
Ограничения на размер входа в условии — подсказка: если n до 10⁵, решение должно быть не хуже O(n log n).
Чего ждут от Junior, Middle и Senior
Junior. Задачи уровня Easy и простые Medium: хеш-таблицы, два указателя, строки, сортировка. Допустимо прийти к оптимуму с подсказками, но рабочий код и верная оценка сложности обязательны.
Middle. Medium-задачи: скользящее окно, бинарный поиск, деревья, BFS/DFS, простое динамическое программирование. Ожидается самостоятельный переход от наивного решения к оптимальному и чистый код без подсказок.
Senior. Алгоритмы обычно те же Medium, иногда Hard, но вес секции ниже: больше времени уходит на проектирование систем. От сеньора ждут быстрого решения, аккуратного кода и обсуждения компромиссов: память против скорости, поведение на реальных данных, распараллеливание.
Паттерны и типовые задачи с решениями
Решения ниже написаны на Python: он компактный и хорошо читается. Логика переносится на любой язык.
Хеш-таблица: первый неповторяющийся символ
Условие. Найдите индекс первого символа строки, который встречается ровно один раз. Если такого нет, верните -1.
Разбор. Два прохода: первый считает частоты, второй ищет первый символ с частотой 1. Время O(n), память O(k), где k — размер алфавита.
from collections import Counter
def first_unique(s: str) -> int:
counts = Counter(s)
for i, ch in enumerate(s):
if counts[ch] == 1:
return i
return -1
Хеш-таблица — первое, о чём стоит подумать, когда нужно «найти пару», «посчитать частоты» или «проверить, встречалось ли».
Два указателя: перенести нули в конец
Условие. Переставьте все нули в конец массива, сохранив порядок остальных элементов. Дополнительную память не использовать.
Разбор. Указатель write показывает, куда поставить следующий ненулевой элемент, read перебирает массив. Время O(n), память O(1).
def move_zeroes(nums: list[int]) -> list[int]:
write = 0
for read in range(len(nums)):
if nums[read] != 0:
nums[write], nums[read] = nums[read], nums[write]
write += 1
return nums
Скользящее окно: самая длинная подстрока без повторов
Условие. Найдите длину самой длинной подстроки без повторяющихся символов: для "abcabcbb" ответ 3.
Разбор. Окно [left, right] расширяем вправо. Если символ уже встречался внутри окна, сдвигаем left за его прошлую позицию. Проверка last_seen[ch] >= left важна: без неё на строке "abba" левая граница уедет назад.
def longest_unique_substring(s: str) -> int:
last_seen = {}
left = best = 0
for right, ch in enumerate(s):
if ch in last_seen and last_seen[ch] >= left:
left = last_seen[ch] + 1
last_seen[ch] = right
best = max(best, right - left + 1)
return best
Сигнал для скользящего окна в условии — «непрерывный подмассив» или «подстрока» и поиск максимума или минимума.
Префиксные суммы: подмассивы с суммой k
Условие. Посчитайте количество непрерывных подмассивов, сумма которых равна k. Числа могут быть отрицательными.
Разбор. Сумма подмассива от i до j равна разности префиксных сумм. Храним, сколько раз встречалась каждая префиксная сумма, и для текущей ищем prefix - k. Скользящее окно здесь не работает из-за отрицательных чисел — об этом стоит сказать вслух.
from collections import defaultdict
def subarray_sum(nums: list[int], k: int) -> int:
count = 0
prefix = 0
seen = defaultdict(int)
seen[0] = 1
for x in nums:
prefix += x
count += seen[prefix - k]
seen[prefix] += 1
return count
Бинарный поиск: первая позиция не меньше target
Условие. В отсортированном массиве найдите индекс первого элемента, который больше или равен target.
Разбор. Ошибки в бинарном поиске — чаще всего на границах. Надёжный шаблон — полуинтервал [lo, hi): цикл до lo < hi, hi = mid без минус единицы. Время O(log n).
def lower_bound(nums: list[int], target: int) -> int:
lo, hi = 0, len(nums)
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] < target:
lo = mid + 1
else:
hi = mid
return lo
Бинарный поиск применяют не только к массивам, но и к ответу: «минимальная скорость, при которой успеем за H часов» ищется перебором скорости бинарным поиском.
Монотонный стек: дни до потепления
Условие. Для каждого дня найдите, сколько дней ждать более тёплой температуры. Если потепления не будет — 0.
Разбор. Стек хранит индексы дней, для которых ответ ещё не найден, температуры в нём убывают. Новый тёплый день «закрывает» все более холодные дни на вершине стека. Каждый индекс попадает в стек и выходит из него один раз, поэтому время O(n).
def daily_temperatures(temps: list[int]) -> list[int]:
answer = [0] * len(temps)
stack = []
for i, t in enumerate(temps):
while stack and temps[stack[-1]] < t:
j = stack.pop()
answer[j] = i - j
stack.append(i)
return answer
Связный список: разворот
Условие. Разверните односвязный список.
Разбор. Идём по списку и перенаправляем ссылку next каждого узла на предыдущий. Главное — сохранить следующий узел до того, как перезаписать ссылку.
def reverse_list(head):
prev = None
cur = head
while cur:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
Вдогонку часто просят рекурсивную версию и спрашивают, почему она тратит O(n) памяти на стек вызовов.
Деревья: проверка дерева поиска
Условие. Проверьте, является ли двоичное дерево деревом поиска (BST): все значения слева от узла меньше него, справа — больше.
Разбор. Частая ошибка — сравнивать узел только с непосредственными детьми. Правильно передавать вниз допустимый диапазон значений.
def is_valid_bst(node, low=float("-inf"), high=float("inf")) -> bool:
if node is None:
return True
if not (low < node.val < high):
return False
return (is_valid_bst(node.left, low, node.val)
and is_valid_bst(node.right, node.val, high))
Графы и BFS: количество островов
Условие. Дана сетка из "1" (суша) и "0" (вода). Посчитайте количество островов — групп суши, соединённых по горизонтали и вертикали.
Разбор. Перебираем клетки; встретив непосещённую сушу, увеличиваем счётчик и обходом в ширину (BFS) помечаем весь остров. Время и память O(rows × cols).
from collections import deque
def num_islands(grid: list[str]) -> int:
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
seen = set()
islands = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == "1" and (r, c) not in seen:
islands += 1
queue = deque([(r, c)])
seen.add((r, c))
while queue:
x, y = queue.popleft()
for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nx, ny = x + dx, y + dy
if (0 <= nx < rows and 0 <= ny < cols
and grid[nx][ny] == "1"
and (nx, ny) not in seen):
seen.add((nx, ny))
queue.append((nx, ny))
return islands
BFS находит кратчайший путь в невзвешенном графе, DFS (обход в глубину) удобен для поиска компонент и циклов. Рекурсивный DFS на большой сетке может упереться в лимит глубины рекурсии — сильный кандидат упомянет это сам.
Куча: k наибольших элементов
Условие. Верните k наибольших элементов массива.
Разбор. Сортировка даёт O(n log n). Мин-куча размера k даёт O(n log k): держим в куче k лучших и выталкиваем минимальный, когда размер превышает k. При n = 10⁷ и k = 10 разница заметна.
import heapq
def top_k(nums: list[int], k: int) -> list[int]:
heap = []
for x in nums:
heapq.heappush(heap, x)
if len(heap) > k:
heapq.heappop(heap)
return sorted(heap, reverse=True)
Интервалы: слияние пересекающихся
Условие. Объедините пересекающиеся интервалы: [[1,3],[2,6],[8,10]] → [[1,6],[8,10]].
Разбор. Сортируем по началу, затем либо расширяем последний интервал результата, либо добавляем новый. Уточните у интервьюера, считаются ли пересекающимися [1,4] и [4,5].
def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:
intervals.sort(key=lambda x: x[0])
merged = []
for start, end in intervals:
if merged and start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
Динамическое программирование: размен монет
Условие. Найдите минимальное количество монет номиналами coins, чтобы набрать сумму amount. Если нельзя — верните -1.
Разбор. Жадный алгоритм (брать самую крупную монету) ошибается: для монет 1, 3, 4 и суммы 6 он даст 4 + 1 + 1, а оптимум — 3 + 3. Динамическое программирование считает ответ для всех сумм от 0 до amount: dp[a] — минимум монет для суммы a. Время O(amount × len(coins)).
def coin_change(coins: list[int], amount: int) -> int:
INF = amount + 1
dp = [0] + [INF] * amount
for a in range(1, amount + 1):
for c in coins:
if c <= a:
dp[a] = min(dp[a], dp[a - c] + 1)
return dp[amount] if dp[amount] != INF else -1
Для динамики на собеседовании полезно проговорить три вещи: что хранит состояние, как оно пересчитывается и какой базовый случай.
Перебор с возвратом: все подмножества
Условие. Верните все подмножества массива уникальных чисел.
Разбор. Backtracking (перебор с возвратом): на каждом шаге добавляем элемент, уходим в рекурсию и убираем его обратно. Подмножеств 2ⁿ, поэтому время O(n · 2ⁿ) — улучшить нельзя, ведь их все нужно вывести.
def subsets(nums: list[int]) -> list[list[int]]:
result = []
path = []
def backtrack(start: int) -> None:
result.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
backtrack(i + 1)
path.pop()
backtrack(0)
return result
Как решать задачу на собеседовании: алгоритм действий
- Перескажите условие своими словами и уточните ограничения: размер входа, диапазон значений, возможны ли дубликаты и пустой вход.
- Разберите 1–2 примера вручную, включая граничный.
- Назовите наивное решение и его сложность. Даже перебор показывает, что вы поняли задачу.
- Ищите узкое место и подходящий паттерн: повторные поиски — хеш-таблица, отсортированные данные — бинарный поиск или два указателя, подмассивы — окно или префиксные суммы.
- Согласуйте подход с интервьюером до того, как писать код.
- Пишите код с понятными именами переменных, проговаривая шаги.
- Проверьте решение на примере, пройдя по коду построчно, и назовите итоговую сложность.
На всё это обычно есть 30–45 минут, поэтому на шаги 1–5 стоит тратить не больше 10–15 минут.
Как готовиться: план на 8 недель
| Недели | Темы | Цель |
|---|---|---|
| 1–2 | Массивы, строки, хеш-таблицы, два указателя, сортировка | 30–40 задач Easy, привычка оценивать сложность |
| 3–4 | Скользящее окно, префиксные суммы, бинарный поиск, стек | 30 задач Easy и Medium |
| 5–6 | Связные списки, деревья, графы, BFS и DFS, куча | 30 задач Medium |
| 7 | Динамическое программирование, backtracking | 15–20 задач Medium |
| 8 | Смешанные задачи на время, пробные интервью | 3–5 мок-интервью вслух |
Принципы, которые экономят время:
- Учите паттерны. Решение конкретной задачи забудется, приём останется. После каждой задачи запишите одной фразой, какой приём её решает.
- Ограничивайте время. 30–40 минут на попытку, потом — разбор чужого решения и повтор через несколько дней.
- Решайте вслух. Секцию проваливают не только из-за незнания, но и из-за молчания.
- Пишите без автодополнения хотя бы часть задач: на интервью редактор может быть простым.
- Делайте мок-интервью с друзьями или на специальных платформах: оценка со стороны быстрее показывает слабые места.
Типичные ошибки кандидатов
- Начинают писать код в первую минуту и уходят в неверное решение.
- Не замечают ограничений и предлагают O(n²) при n до 10⁵.
- Ошибаются на границах: пустой массив, один элемент, переполнение,
midв бинарном поиске. - Молчат, и интервьюер не может помочь подсказкой.
- Не проверяют код и сдают решение с опечаткой, которую заметили бы за минуту трассировки.
- Заучивают решения популярных задач и теряются на небольшой модификации условия.
Live-coding, онлайн-контест и тестовое задание
Live-coding — классическая алгоритмическая секция с интервьюером: 45–60 минут, одна-две задачи, общий редактор. Оценивают и решение, и рассуждения.
Онлайн-контест (скрининг) — задачи с автоматической проверкой на платформе вроде Яндекс Контеста, HackerRank или Codility. Его проходят без интервьюера, обычно за 1–3 часа. Важны только тесты: решение должно проходить ограничения по времени и памяти.
Тестовое задание алгоритмы проверяет редко: это проект, где смотрят на архитектуру и качество кода. Если компания дала тестовое вместо алгоритмической секции, готовиться нужно иначе — к ревью кода и вопросам по проекту.
ИИ-ассистенты на алгоритмической секции
Большинство алгоритмических интервью по-прежнему проводятся без ИИ-ассистентов, и правило стоит уточнить у рекрутера заранее. Но формат меняется: Meta в 2025 году начала тестировать собеседования с разрешённым ИИ, Canva разрешила кандидатам ИИ на технических интервью, а Google во втором полугодии 2026 года пилотирует для части инженерных вакансий в США раунд с ассистентом Gemini, где вместо задачи с нуля нужно разобраться в существующем коде, найти ошибки и оптимизировать его.
Обратная тенденция тоже есть: по данным отчёта Greenhouse за 2025 год, 39% нанимающих менеджеров в США перенесли часть собеседований в офис из-за подозрений, что кандидаты на удалённых этапах пользуются ИИ.
Как распознают подсказки от нейросети, когда они запрещены: кандидат пишет оптимальное решение без промежуточных шагов, но не может объяснить инвариант цикла или оценить сложность; спотыкается, когда интервьюер меняет условие; делает долгие паузы с отведённым взглядом. Любое изменение задачи «а если числа могут быть отрицательными?» быстро показывает, понимает ли человек решение.
Если ИИ разрешён, оценивают другое: умение разбить задачу, сформулировать запрос, проверить сгенерированный код на граничных случаях и найти в нём ошибку. Этот навык всё чаще встречается в требованиях вакансий — подробнее в материалах о вайб-кодинге и ИИ-навыках программиста.
Где тренироваться
| Ресурс | Что там есть | Кому подходит |
|---|---|---|
| LeetCode | Тысячи задач, подборки по компаниям, еженедельные контесты | Основной тренажёр для подготовки к секции |
| NeetCode | Список задач, сгруппированных по паттернам, видеоразборы | Тем, кто хочет готовиться по паттернам |
| CodeRun | Тренажёр Яндекса с задачами для подготовки к собеседованиям | Тем, кто идёт в российские компании |
| Codeforces | Соревнования и архив задач с рейтингом | Тем, кто хочет прокачать скорость и сложные задачи |
| Codewars | Задачи-«каты» по уровням, чужие решения | Новичкам для разогрева |
| HackerRank | Задачи по темам, формат онлайн-скринингов | Подготовке к автоматическим тестам компаний |
Теорию удобно освежать по книге «Грокаем алгоритмы» Адитьи Бхаргавы — она объясняет базовые структуры данных на картинках. Для серьёзной подготовки к секции этого мало, но как вход в тему книга подходит.
Алгоритмическую секцию проходят разработчики, ML-инженеры и часть аналитиков. Посмотреть, какие компании сейчас нанимают, можно в каталоге вакансий в разработке, а перед откликом — обновить резюме.
Частые вопросы
Сколько задач на LeetCode нужно решить перед собеседованием?
Ориентир — 100–150 задач уровня Easy и Medium, покрывающих основные паттерны. Если вы решаете новую Medium-задачу за 30 минут без подсказок, вы готовы к большинству секций.
Какой язык выбрать для алгоритмической секции?
Тот, на котором пишете быстрее всего. Python популярен из-за краткости, Java и C++ — из-за привычных структур данных. Компании обычно разрешают любой распространённый язык.
Нужно ли учить Hard-задачи?
Для большинства компаний — нет. Hard встречаются на позициях с сильным алгоритмическим уклоном и в отдельных компаниях. Лучше уверенно решать Medium.
Что делать, если не успел решить задачу?
Доведите до конца хотя бы наивное решение и объясните, как бы его улучшили. Частичное решение с ясными рассуждениями часто оценивают выше, чем молчаливую попытку сразу написать оптимальное.
Можно ли подготовиться за неделю?
За неделю можно освежить базовые паттерны, если раньше уже решали задачи. С нуля за такой срок секцию в крупной компании пройти сложно: закладывайте хотя бы 1–2 месяца регулярной практики.
Пригодится ли спортивное программирование?
Опыт олимпиад помогает быстро находить идеи, но на собеседовании важнее чистый код и коммуникация. Олимпиадные привычки вроде однобуквенных переменных и отсутствия проверок на интервью лучше оставить.
