Skip to content

Вопросы на собеседовании: Инженер-программист

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior.

Смотреть пример резюме: Инженер-программист

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

big-o

Нотация Big O показывает, как время работы или потребление памяти алгоритма растёт вместе с размером входных данных.

  • Она отбрасывает константы и младшие члены, чтобы инженеры могли сравнивать подходы независимо от железа.
  • Проход за O(n) с ростом данных обгонит вложенный цикл за O(n^2), даже если на маленьком входе второй вариант быстрее.
  • Она помогает заранее понять, когда код для 100 строк станет слишком медленным или прожорливым на миллионе строк.

Зачем это спрашивают: Интервьюер проверяет, умеете ли вы рассуждать о масштабируемости абстрактно, а не просто мерить время на маленьких входах.

data-structures

Массив удобен для быстрого доступа по индексу, а связный список для вставки или удаления уже известных узлов.

  • Массив хранит элементы непрерывно, поэтому даёт доступ по индексу за O(1) и эффективно использует кэш.
  • Вставка в середину массива стоит O(n) из-за сдвига элементов, а изменение связного списка у известного узла занимает O(1).
  • Доступ к k-му узлу списка требует O(n), поэтому массив выбирают для произвольного доступа, а список для частых изменений в известных позициях.

Зачем это спрашивают: Сильный ответ связывает раскладку в памяти с конкретной стоимостью доступа, а слабый просто пересказывает определения без компромисса O(1) против O(n).

data-structures

Хеш-таблица достигает среднего поиска за O(1), вычисляя по ключу индекс корзины в базовом массиве.

  • Прямой переход к корзине избавляет от просмотра всех записей.
  • Равномерное хеширование и низкий коэффициент заполнения оставляют в каждой корзине лишь несколько записей.
  • O(1) является средней оценкой, а не гарантией, поскольку плохой хеш или высокая заполненность могут ухудшить поиск до O(n).

Зачем это спрашивают: Это проверяет, понимаете ли вы, что O(1) зависит от хорошего распределения хеша и коэффициента заполнения, а не работает как по волшебству.

data-structures

Коллизия в хеш-таблице возникает, когда разные ключи попадают в один индекс корзины.

  • При раздельном сцеплении конфликтующие записи хранятся в небольшой коллекции внутри корзины и просматриваются при поиске.
  • При открытой адресации таблица проверяет другие ячейки с помощью линейного, квадратичного или двойного хеширования.
  • Java HashMap использует сцепление и может превращать длинные цепочки в сбалансированные деревья, сохраняя худший поиск около O(log n).

Зачем это спрашивают: Интервьюер хочет увидеть, что вы понимаете неизбежность коллизий и можете назвать хотя бы одну конкретную стратегию их разрешения, а не считаете, что ключи никогда не совпадают.

data-structures

Стек извлекает элементы по принципу LIFO, а очередь по принципу FIFO.

  • Стек первым возвращает последний добавленный элемент, как при вложенных вызовах функций или переходе назад в браузере.
  • Очередь первой возвращает самую раннюю задачу, как в очереди печати или пуле воркеров.
  • Обе структуры могут давать вставку и удаление за O(1), но забирают элементы с разных концов последовательности.

Зачем это спрашивают: Сильный кандидат сопоставляет каждой структуре настоящий сценарий использования, показывая, что связывает абстрактный порядок с реальными системами.

algorithms

Бинарный поиск находит значение в отсортированных данных, отбрасывая половину оставшегося диапазона после каждого сравнения.

  • Он сравнивает искомое значение со средним элементом и работает за O(log n).
  • Данные должны быть заранее отсортированы по ключу поиска, иначе нельзя правильно выбрать отбрасываемую половину.
  • Предварительная сортировка стоит O(n log n), поэтому бинарный поиск особенно выгоден для повторных запросов к одним отсортированным данным.

Зачем это спрашивают: Забыть про условие отсортированности значит допустить классическую ошибку, которую этот вопрос призван выявить.

algorithms

Поиск в сбалансированном двоичном дереве занимает O(log n), потому что каждое сравнение отбрасывает целое поддерево.

  • Число шагов поиска ограничено полной высотой дерева от корня до листа.
  • В сбалансированном дереве высота остаётся пропорциональной log n.
  • Разбалансированное дерево может выродиться в связный список с поиском за O(n), поэтому AVL- и красно-чёрные деревья поддерживают баланс.

Зачем это спрашивают: Различие между сбалансированным O(log n) и вырожденным O(n) как раз и отличает точный ответ от размытого.

recursion

Рекурсия представляет собой решение задачи через вызов той же функции для уменьшенной версии этой задачи.

  • Базовый случай должен гарантированно останавливать дальнейшую цепочку рекурсивных вызовов.
  • Рекурсивный случай должен уменьшать вход и приближаться к базовому случаю.
  • Отсутствующий или недостижимый базовый случай вызывает бесконечные вызовы и переполнение стека, поскольку каждый вызов расходует память.

Зачем это спрашивают: Назвать и базовый случай, и продвижение к нему показывает, что вы понимаете, почему рекурсия завершается, а не просто как она выглядит.

data-structures

Дерево является ограниченным видом графа, а граф может описывать более общие связи.

  • Граф состоит из узлов и рёбер и может быть ориентированным или неориентированным, с циклами или без них.
  • Дерево связно, не содержит циклов и имеет ровно один путь между каждой парой узлов.
  • В дереве из n узлов есть n минус одно ребро, поэтому каждое дерево является графом, но не каждый граф является деревом.

Зачем это спрашивают: Сильный ответ подаёт дерево как ограниченный граф, показывая, что вы улавливаете иерархию, а не считаете их несвязанными.

algorithms

Поиск в ширину обходит граф по уровням, а поиск в глубину идёт по одной ветке до возврата назад.

  • BFS использует очередь и находит кратчайшие пути в невзвешенном графе.
  • DFS использует стек или рекурсию и подходит для поиска циклов, топологической сортировки и перебора всех путей.
  • Оба работают за O(V + E), но память BFS зависит от самого широкого уровня, а память DFS от самого глубокого пути.

Зачем это спрашивают: Интервьюер ищет механизм очередь против стека плюс корректную задачу, где каждый из них подходит лучше.

algorithms

Quicksort разбивает массив относительно опорного элемента и рекурсивно сортирует две полученные части.

  • Элементы меньше опорного перемещаются влево, а элементы больше него вправо.
  • Удачные опорные элементы делят массив примерно поровну и дают среднюю сложность O(n log n).
  • Постоянный выбор крайнего элемента приводит к O(n^2), хотя случайный выбор или медиана из трёх делают это маловероятным на практике.

Зачем это спрашивают: Знание и средней O(n log n), и худшей O(n^2), плюс того, что её вызывает, показывает глубину сверх заучивания одного числа.

algorithms

Стабильный алгоритм сортировки сохраняет взаимный порядок элементов, равных по ключу сравнения.

  • Стабильность важна, когда сортировки последовательно применяются по нескольким ключам.
  • После сортировки по дате и стабильной сортировки по имени записи с одинаковыми именами сохраняют порядок дат.
  • Сортировка слиянием стабильна, а наивный quicksort нет, поэтому выбор алгоритма влияет на сохранение предыдущего порядка.

Зачем это спрашивают: Сильный ответ приводит сценарий сортировки по нескольким ключам, доказывая, что стабильность является практическим свойством, а не просто мелочью для эрудиции.

memorydata-structures

Стек хранит краткоживущие данные вызовов, а куча хранит динамически выделенные данные с более гибким временем жизни.

  • Стек содержит кадры вызовов, локальные переменные и адреса возврата и автоматически очищается в порядке LIFO.
  • Выделение в стеке быстрое, но его размер ограничен, тогда как куча больше, медленнее и подвержена фрагментации и утечкам.
  • Данные в куче могут пережить создавшую их функцию, а данные стека обычно привязаны к области видимости и времени вызова.

Зачем это спрашивают: Это проверяет, связываете ли вы обе области со временем жизни и стоимостью выделения, а не путаете их со структурами данных стек и куча.

algorithms

Я бы проверил палиндром двумя указателями, которые движутся к центру строки с противоположных концов.

  • На каждом шаге сравнивается пара символов, а первое несовпадение сразу даёт false.
  • Подход работает за O(n) времени и O(1) дополнительной памяти, в отличие от развёрнутой копии с O(n) лишней памяти.
  • Если этого требует определение палиндрома, я бы сначала выровнял регистр и исключил неалфавитно-цифровые символы.

Зачем это спрашивают: Именно решение с двумя указателями и O(1) памяти, а не наивный разворот со сравнением, отличает эффективный ответ от рабочего, но расточительного.

memorydata-structures

Алгоритм черепахи и зайца Флойда обнаруживает цикл в связном списке без дополнительного хранилища.

  • Один указатель продвигается на один узел за шаг, а второй на два.
  • При наличии цикла быстрый указатель догонит медленный, а достижение null означает отсутствие цикла.
  • Алгоритм работает за O(n) времени и O(1) памяти, тогда как множество посещённых узлов потребовало бы O(n) памяти.

Зачем это спрашивают: Условие без дополнительной памяти проверяет ровно одно: назовёте ли вы приём с двумя указателями и его O(1) памяти.

data-structures

Стек естественно подходит для отмены, потому что последнее действие нужно обратить первым.

  • Каждое действие кладёт предыдущее состояние или обратную команду на стек отмены.
  • Операция отмены извлекает и применяет верхнюю запись в порядке LIFO.
  • Второй стек поддерживает повтор, сохраняя отменённые действия до их нового применения.

Зачем это спрашивают: Интервьюер хочет рассуждение про LIFO, которое связывает порядок отмены со стеком, плюс бонусное понимание второго стека для повтора.

data-structures

Добавление в динамический массив имеет амортизированную сложность O(1), потому что дорогие расширения происходят всё реже.

  • Заполненный массив обычно удваивает ёмкость и копирует все n элементов за O(n).
  • Экспоненциальный рост оставляет много дешёвых добавлений за O(1) между расширениями.
  • Распределение общей стоимости копирования между всеми добавлениями даёт постоянную среднюю цену операции.

Зачем это спрашивают: Этот вопрос выделяет понимание амортизированного анализа: удвоение делает дорогой шаг достаточно редким, чтобы стоимость усреднилась до константы.

data-structures

Множество представляет собой неупорядоченную коллекцию уникальных элементов с быстрой проверкой принадлежности.

  • Множество на основе хеш-таблицы обычно проверяет наличие за O(1), тогда как списку нужен проход за O(n).
  • Множество лучше списка, когда требуется удалить дубликаты или многократно проверять наличие значения.
  • Типичные примеры включают удаление повторных ID пользователей и учёт посещённых значений при обходе графа.

Зачем это спрашивают: Сильный ответ выделяет проверку принадлежности за O(1) как причину предпочесть множество, а не только то, что оно убирает дубликаты.

Повторное склеивание строк часто медленно, поскольку неизменяемая строка при каждом добавлении копируется в новое значение.

  • В Java и Python такое построение растущей строки в цикле может суммарно стоить O(n^2).
  • Изменяемый буфер вроде StringBuilder в Java или список с последующим str.join в Python дёшево накапливает части.
  • Однократное создание итоговой строки в конце снижает общую стоимость до O(n).

Зачем это спрашивают: Настоящее понимание проявляется в том, чтобы распознать скрытую O(n^2) от копий неизменяемых строк и назвать O(n) фикс на основе буфера.

data-structures

Куча является очередью с приоритетом, которая держит наименьший или наибольший элемент в корне.

  • Она даёт доступ к крайнему элементу за O(1), а вставку и удаление за O(log n).
  • Она эффективно возвращает следующий по приоритету элемент без полной сортировки всех данных.
  • Кучу применяют для планирования задач, слияния отсортированных потоков и выбора ближайшего непосещённого узла в алгоритме Дейкстры.

Зачем это спрашивают: Интервьюер проверяет, связываете ли вы операции кучи за O(log n) с задачей повторного извлечения минимума или максимума, а не определяете её в отрыве от контекста.

Закрытые вопросы

  • 21

    Каковы четыре столпа объектно-ориентированного программирования?

    oop
  • 22

    В чём разница между классом и объектом?

    oop
  • 23

    В чём разница между наследованием и композицией, и почему композицию часто предпочитают?

    oopownership
  • 24

    Что такое полиморфизм? Приведите конкретный пример.

    oop
  • 25

    Что такое инкапсуляция и какие проблемы она предотвращает?

    oop
  • 26

    В чём разница между абстрактным классом и интерфейсом?

    types
  • 27

    В чём разница между перегрузкой и переопределением метода?

    oop
  • 28

    Назовите принципы SOLID и подробно объясните один из них.

    solid
  • 29

    Что такое внедрение зависимостей и почему это полезно?

    injectiondependencies
  • 30

    Что такое статический метод и когда его уместно использовать?

    oop
  • 31

    Каковы ключевые различия между объектно-ориентированным и функциональным программированием?

    oopfunctional
  • 32

    Что такое чистая функция и почему чистые функции проще тестировать?

    functional
  • 33

    В чём разница между компилируемыми и интерпретируемыми языками?

    fundamentals
  • 34

    В чём разница между статической и динамической типизацией и каковы компромиссы?

    types
  • 35

    Как в общих чертах работает сборка мусора?

    gc
  • 36

    Что такое утечка памяти и может ли она возникнуть в языке со сборкой мусора?

    memorygc
  • 37

    Как работает обработка исключений и какие есть хорошие практики использования try/catch?

    error-handling
  • 38

    В чём разница между передачей по значению и передачей по ссылке?

    fundamentals
  • 39

    Почему null-значения частый источник багов и как от них защищаться?

    fundamentals
  • 40

    В чём разница между изменяемыми и неизменяемыми данными и почему неизменяемость помогает?

    immutability
  • 41

    Что такое область видимости переменной и почему глобальные переменные не приветствуются?

    scope
  • 42

    Что такое состояние гонки и в каких ситуациях с ним может столкнуться джуниор?

    concurrency
  • 43

    Зачем команды используют систему контроля версий и какие проблемы она решает?

    git
  • 44

    В чём разница между git merge и git rebase?

    git
  • 45

    Что такое конфликт слияния и как его безопасно разрешить?

    git
  • 46

    Опишите работу с feature-веткой от начала задачи до её слияния.

    git
  • 47

    Что на самом деле делает git pull под капотом?

    git
  • 48

    Как отменить уже закоммиченное изменение и когда использовать revert вместо reset?

    git
  • 49

    Что делает коммит хорошим с точки зрения и размера, и сообщения?

    git
  • 50

    Для чего нужен .gitignore и какие файлы никогда нельзя коммитить?

    git
  • 51

    В чём разница между юнит-тестами, интеграционными и сквозными (end-to-end) тестами?

    e2e
  • 52

    Какие свойства делают юнит-тест хорошим?

    unit
  • 53

    В чём разница между моком и стабом и когда их использовать?

    mocking
  • 54

    Что такое разработка через тестирование (TDD) и как выглядит цикл красный-зелёный-рефакторинг?

    refactoring
  • 55

    Что такое покрытие кода и почему 100 процентов покрытия не гарантирует качество?

    coverage
  • 56

    Вы тестируете функцию, которая делит два числа. Какие тест-кейсы вы напишете?

    test-cases
  • 57

    Почему при исправлении бага стоит написать тест?

  • 58

    Что такое флаки-тест и как с ним справляться?

    flaky
  • 59

    Опишите ваш порядок действий, когда код бросает ошибку, которую вы раньше не видели.

    concurrency
  • 60

    Когда отладчик эффективнее print-ов, а когда print-ы вполне подходят?

    debugging
  • 61

    Как отлаживать проблему, которая воспроизводится в продакшене, но не на вашей машине?

    debugging
  • 62

    Что такое стек-трейс и как читать его, чтобы найти источник ошибки?

    debugging
  • 63

    Вчера код работал, а сегодня сломан. Как найти, какое изменение его сломало?

    debugging
  • 64

    Пользователь сообщает о баге, который вы не можете воспроизвести. Каковы ваши дальнейшие шаги?

    debugging
  • 65

    Что такое первичный ключ и что делает его хорошим?

    primary-keys
  • 66

    В чём разница между INNER JOIN и LEFT JOIN?

    joins
  • 67

    Для чего нужны агрегатные функции и GROUP BY? Приведите пример.

    aggregation
  • 68

    Что такое нормализация базы данных и чем она полезна?

    databasenormalization
  • 69

    Что такое транзакция и что гарантируют свойства ACID?

    transactionsacid
  • 70

    Что такое SQL-инъекция и как её предотвратить?

    sqlinjection
  • 71

    В чём разница между WHERE и HAVING?

    queriesaggregation
  • 72

    Когда вы добавите индекс к таблице и во что обходится индекс?

    indexes
  • 73

    Почему условие column = NULL в SQL никогда ничему не соответствует и что использовать вместо него?

    sqlschemafundamentals
  • 74

    Как найти дублирующиеся строки в таблице с помощью SQL?

    dedupsql
  • 75

    Что происходит, когда вы вводите URL в браузер и нажимаете Enter?

    http
  • 76

    Какие HTTP-методы встречаются чаще всего и какие из них идемпотентны?

    httpidempotency
  • 77

    Что означают классы HTTP-статусов 2xx, 3xx, 4xx и 5xx?

    httpstatus-codes
  • 78

    В чём разница между HTTP и HTTPS?

    httphttps
  • 79

    Что такое API и что делает API RESTful?

    rest
  • 80

    Почему JSON стал форматом обмена данными по умолчанию для веб-API?

    api
  • 81

    Что такое куки и для чего их обычно используют?

    cookies
  • 82

    Что такое DNS и что происходит во время DNS-запроса?

    dns
  • 83

    Ваш код работает локально, но падает в CI. Как вы будете разбираться?

    debugging
  • 84

    Вы попадаете в большую незнакомую кодовую базу, и вам нужно добавить функциональность. Как вы подойдёте к задаче?

    learningjoins
  • 85

    На что вы смотрите, когда ревьюите чужой pull request?

    code-review
  • 86

    Функция в вашем сервисе работает медленно. Как вы выясните причину, прежде чем оптимизировать?

    optimization
  • 87

    Вам нужна функциональность, которую даёт сторонняя библиотека. Как вы решаете между библиотекой и своей реализацией?

    decision-makingdependencies
  • 88

    Почему важны имена и как вы подбираете хорошие имена для переменных и функций?

    clean-code
  • 89

    Что вы проверяете перед тем, как отправить код на ревью?

    code-review
  • 90

    Вы получили задачу с неясными требованиями. Что вы сделаете перед тем, как писать код?

    requirements
  • 91

    Расскажите о сложном баге, который вы починили. Как вы его выследили и чему научились?

    story
  • 92

    Как вы реагируете, когда код-ревью указывает на серьёзные проблемы в вашей работе?

    code-reviewreact
  • 93

    Вы застряли на задаче на несколько часов. Когда и как вы просите о помощи?

    problem-solving
  • 94

    Как вы расставляете приоритеты, когда несколько задач кажутся срочными?

    prioritization
  • 95

    Расскажите о случае, когда вы что-то сломали. Как вы с этим справились?

    story
  • 96

    Как вы подходите к изучению технологии, которую никогда не использовали?

    learning
  • 97

    Как вы оцениваете задачу, которую никогда раньше не делали?

    estimation
  • 98

    Вы не согласны с техническим подходом коллеги. Что вы делаете?

    conflict
  • 99

    Что значит, что задача сделана, помимо того что код компилируется?

    definition-of-done
  • 100

    Как вы объясняете техническую проблему нетехническому человеку?

    communication