Алгоритмы на массивах · модуль 8 · два указателя
Глава 8.3
Уточняющие вопросы
до решения
В этой главе разобрано, какие вопросы задают интервьюеру до того, как писать код, и что меняет в решении каждый ответ. Основной пример — задача 1 о паре с заданной суммой: её условие уточняется в разговоре с интервьюером. Дальше — вопросы к остальным задачам модуля, общий список для любой задачи на массив и тестовые примеры, которые записывают до кода.
- Категория
- Работа с условием
- Уровень
- Начальный
- Опирается на
- Главы 8.1 и 8.2; занятие 7: вопросы к условию
- Зачем это знать
- Решать ту задачу, которую поставили, со всеми её крайними случаями
§1Зачем уточнять условие
Условие на собеседовании часто звучит коротко, и часть деталей в нём не названа. В советах кандидатам из журнала Яндекс Образования про это сказано первым пунктом.
«Задавайте вопросы. Задачу могут поставить абстрактно, не уточняя, что в качестве входных данных могут быть, например, отрицательные числа, а вектор может быть пустым… Спрашивайте, что нужно оптимизировать, какие ограничения по времени и памяти нужно учесть при выборе алгоритма».
Для двух указателей вопросы особенно важны: техника работает только при определённых свойствах данных. Если не спросить, отсортирован ли список, можно написать верный по виду код, который на неотсортированном входе пропустит ответ.
У каждого вопроса ниже есть последствие в решении: новая строка кода, граница цикла или тест. Вопросы о легенде задачи, после которых в решении ничего не меняется, можно не задавать.
§2Разговор с интервьюером о задаче 1
Слева — условие задачи 1 в том виде, в каком его произнёс интервьюер. Справа — вопросы, которые можно задать. После каждого вопроса в разговоре появляются ответ интервьюера и то, что этот ответ меняет в решении, а под условием слева — новое уточнение. Порядок вопросов любой.
§3Что меняет каждый ответ
После разговора условие становится точным, и его можно записать как контракт функции: что она принимает, что возвращает и чего не делает со входом. В таблице для каждого ответа указано, какую часть решения он определяет.
| Ответ интервьюера | Куда попадает | Что это значит для решения |
|---|---|---|
| Цены по возрастанию | Выбор способа | По порядку видно, какую цену можно отбросить, поэтому перебор всех пар не обязателен |
| Товар нельзя взять дважды | Границы поиска | Пара — два разных номера, i < j |
Пары нет — None | Результат | Второй исход функции, у него свой тест |
| Каталог может быть пустым | Тест | Пустой список и список из одного товара дают None |
| Память O(1), вход не меняется | Ограничение на способ | Ни копии, ни словаря, ни sorted(); тест проверяет, что список после вызова тот же |
| Вернуть индексы | Формат результата | Пара номеров (i, j), сортировать копию нельзя |
def pair_sum(prices, target): """Указатели навстречу. Задача 1. prices — цены по возрастанию, повторы возможны, список может быть пустым. Возвращает пару индексов (i, j), i < j, с prices[i] + prices[j] == target, или None, если такой пары нет. Список не меняется. Время O(n), память O(1). """
§4Вопросы к остальным задачам модуля
У задач 2 и 3 вопросы другие: они касаются того, что остаётся в списке после вызова и что функция возвращает. Разговор строится так же — каждый вопрос заканчивается изменением в решении.
| Задача | Вопрос | Типичный ответ | Что меняет |
|---|---|---|---|
| 2 · повторы | Список точно отсортирован? | Да | Одинаковые значения стоят рядом: сравнение с последним записанным находит все повторы |
| Вернуть число или сам список? | Число оставшихся | Функция возвращает длину очищенной части | |
| Хвост за последним значением нужно убрать? | Да, список должен стать короче | После прохода лишний хвост удаляется одной операцией | |
| 3 · сжатие | Серию длины 1 записывать с числом 1? | Нет, одним символом | Длина пишется только у серий из двух и более символов |
| Серия длиннее 9 — как писать длину? | Каждой цифрой отдельно: 12 → «1», «2» | Длина раскладывается на цифры; нужен тест с серией из 12 символов | |
| Может ли один символ встретиться в двух сериях? | Да: «aabaa» → «a2ba2» | Серия заканчивается на первом другом символе, счёт не суммируется | |
| нули в конец | Порядок ненулевых сохранять? | Да | Решение должно сохранять порядок, перестановки с двух концов не годятся |
| Что вернуть? | Ничего, список меняется на месте | Функция возвращает None, как list.sort() |
§5Общий список для задачи на массив
Эти вопросы подходят к любой задаче на массив. На собеседовании задают те из них, ответ на которые не очевиден из условия. Порядок в списке — от тех, что меняют выбор техники, к тем, что меняют отдельные строки.
До кода выяснено
None, -1, пустой список или исключение.§6Тестовые примеры до кода
Следующий шаг после вопросов — записать несколько входов с ответами. Яндекс в описании секции ставит его первым: «Вместо того чтобы сразу писать код, сначала придумайте тестовые примеры». Примеры проверяют, что условие понято одинаково, и потом превращаются в тесты. Для задачи 1 хватает семи примеров, каждый проверяет свой класс входа.
| prices | target | Ответ | Что проверяет |
|---|---|---|---|
[300, 450, 700, 900, 1200, 1500] | 1600 | (2, 3) | обычный случай, пара внутри списка |
[100, 200, 300, 400] | 500 | (0, 3) или (1, 2) | пар несколько, подходит любая |
[300, 450, 700, 900] | 100 | None | пары нет |
[] | 500 | None | пустой вход |
[250] | 500 | None | один товар: пары нет, хотя 250 + 250 = 500 |
[250, 250] | 500 | (0, 1) | одинаковые цены у разных товаров |
[-7, -3, 0, 2, 9] | −1 | (1, 3) | отрицательные числа |
Все семь примеров вошли в tests.py стартового набора. Ответы в таблице получены запуском эталонного решения на Python 3.12.
§7Проверьте себя
Вопрос 1Интервьюер ответил: «Каталог может быть пустым». Что из этого следует для решения?
Интервьюер назвал этот случай прямо, значит, на нём будут проверять. Функция должна вернуть
Noneи не упасть.Верно. В пустом каталоге пар нет. Этот вход записывают в примеры до кода и проверяют отдельным тестом.
Цены уже отсортированы по условию, а пустой список сортировать незачем. Пустой вход влияет только на результат.
Вопрос 2Какой из вопросов к задаче 1 не меняет ни решения, ни тестов?
Меняет границы поиска: пара — только два разных номера, и нужен тест на цену, равную половине суммы.
Верно. Это вопрос о легенде: ответ на него не влияет ни на технику, ни на границы, ни на тесты.
Добавляет второй исход функции —
None— и тест на него.Меняет выбор способа: при миллионе товаров перебор всех пар не проходит.