Подготовка к алгоритмическим задачам

Как найти баг в своём коде на собеседовании без отладчика
Коротко
| Шаг | Действие |
|---|---|
| 1 | Не паниковать, сказать вслух |
| 2 | Взять минимальный пример |
| 3 | Выписать переменные по итерациям |
| 4 | Проверить типовые места |
| 5 | Исправить и прогнать заново |
Умение найти свою ошибку сильно улучшает впечатление. Интервьюеры специально смотрят, как вы себя ведёте, когда что-то не работает.
Почему это отдельный навык
В обычной работе баг ищет отладчик: поставил точку останова, посмотрел значения. На собеседовании чаще всего нет ни запуска, ни отладчика — только код в документе и ваша голова.
При этом читать собственный код объективно человек не умеет: он видит то, что хотел написать, а не то, что написал. Поэтому нужен формальный метод, а не «посмотрю внимательно ещё раз».
Шаг 1. Сказать вслух
Первое, что нужно сделать, — озвучить.
«Так, на этом примере должно вернуться два, а по моему коду получается три. Давайте пройду по шагам».
Это даёт три вещи: снимает напряжение, показывает интервьюеру, что вы контролируете ситуацию, и часто провоцирует подсказку.
Молчаливое залипание в код на две минуты выглядит гораздо хуже найденного бага.
Шаг 2. Минимальный пример
Не берите пример из условия — он обычно слишком большой. Возьмите самый маленький вход, на котором ошибка воспроизводится.
Для массива это два-три элемента. Для дерева — три узла. Для строки — четыре символа.
Часто уже на этом шаге ошибка становится очевидной: на входе из двух элементов легко заметить, что цикл делает лишнюю итерацию.
Шаг 3. Трассировка на бумаге
Основной метод. Выписывайте значения переменных на каждой итерации, не в голове, а на бумаге или в документе.
nums = [1, 2, 3, 4], target = 6
итерация i x need seen результат
1 0 1 5 {} нет → seen={1:0}
2 1 2 4 {1:0} нет → seen={1:0, 2:1}
3 2 3 3 {1:0, 2:1} нет → seen={..., 3:2}
4 3 4 2 {1:0, 2:1, 3:2} есть! return [1, 3]
Пять строк — и видно, работает ли логика. Держать это в голове невозможно, а на бумаге ошибка вылезает сама.
Дополнительный эффект: интервьюер видит вашу таблицу и понимает, что вы делаете. Это тоже оценивается.
Шаг 4. Проверить типовые места
Большинство багов на интервью сидят в одних и тех же местах. Пробегитесь по списку.
Границы циклов
for i in range(len(nums)): # до последнего включительно for i in range(len(nums) - 1): # без последнего for i in range(1, len(nums)): # без первого
Спросите себя: должен ли последний элемент обрабатываться? А первый?
Классика: nums[i + 1] внутри цикла до len(nums) — выход за границу на последней итерации.
Условия сравнения
< против <= — источник половины ошибок на единицу.
В бинарном поиске: while left <= right или while left < right — от этого зависит, найдётся ли элемент, стоящий последним.
В двух указателях: while left < right обычно правильно, <= даст сравнение элемента с самим собой.
Инициализация
best = 0 # сломается на массиве из отрицательных чисел best = nums[0] # сломается на пустом массиве best = float('-inf') # обычно правильно
Спросите: а если все числа отрицательные? А если массив пуст?
Решай алгоритмические задачи как профи

Порядок операций
В связных списках порядок присваиваний критичен: сохранили next до изменения ссылки или после?
В скользящем окне: обновили ответ до сжатия окна или после?
Возвращаемое значение
Что возвращается, если ничего не найдено? Пустой список, -1, None? И совпадает ли это с условием?
Изменение при итерации
Удаление из списка во время прохода по нему сдвигает индексы и пропускает элементы. Частая и незаметная ошибка.
Шаг 5. Исправить и прогнать заново
После правки обязательно прогоните пример ещё раз, а не просто скажите «теперь должно работать».
И проверьте, что исправление не сломало другой случай — особенно если вы поменяли границу или условие.
Что делать, если баг не находится
Объясните код вслух построчно. Метод «резинового утёнка» работает: проговаривая, вы читаете то, что написано, а не то, что имели в виду. Ошибка часто вылезает на середине объяснения.
Упростите. Уберите оптимизацию, напишите в лоб. Если наивная версия работает, значит баг в оптимизации, и область поиска сузилась.
Проверьте предположения. «Я считаю, что массив отсортирован — это точно так?» Иногда баг не в коде, а в понимании условия.
Признайте и двигайтесь. Если время уходит: «вижу, что здесь ошибка на границе, но не могу найти точное место. Логика в целом такая-то». Это лучше, чем сжечь оставшиеся десять минут.
Чего делать не стоит
Переписывать всё с нуля. Соблазн велик, но обычно вы напишете тот же баг заново, потратив время.
Молча тыкать наугад. Менять < на <= и обратно, надеясь, что заработает. Видно сразу и производит плохое впечатление.
Спорить, что код верный. Если интервьюер показал контрпример — проверьте его руками. Он почти наверняка прав.
Игнорировать подсказку. «А что будет на пустом массиве?» — это не праздный вопрос, а прямое указание, где искать.
Как тренировать
Решайте без запуска. Пишите код в блокноте и прогоняйте руками до того, как проверить. Обнаружите, сколько ошибок обычно ловил за вас интерпретатор.
Ведите список своих типовых багов. У каждого он свой: кто-то регулярно ошибается на границах, кто-то забывает пустой ввод. Через десять задач список стабилизируется — и станет вашим личным чек-листом.
Проверяйте на двух элементах. Возьмите привычку прогонять любое решение на минимальном входе. Большая часть ошибок вылезает именно там.
Что запомнить
- Умение найти свой баг оценивается отдельно и высоко.
- Первым делом скажите вслух, что нашли расхождение.
- Трассировка на бумаге заменяет отладчик: выписывайте переменные по итерациям.
- Типовые места: границы циклов,
<против<=, инициализация, порядок операций. - Объяснение кода вслух построчно часто вскрывает ошибку само.
- Не переписывайте с нуля и не меняйте условия наугад.
