Модуль 8 · Два указателя · ОП.04

Алгоритмы на массивах · модуль 8 · два указателя

Глава 8.12

8.12

Практика, шпаргалка
и домашнее задание

В этой главе работа на паре по стартовому набору lesson_08: пять функций, 37 тестов и замер. Затем вопросы для review по КТП, шпаргалка, словарь модуля и домашнее задание: довести решения, найти три ошибки на границе и решить новую задачу на указатели навстречу. В конце — порядок сдачи и критерии.

Категория
Практика
Время на паре
Около 50 минут
Проверка
pytest, 37 тестов
Результат
Папка portfolio/lesson_08 с решениями, замером и заметкой

§1Стартовый набор

В архиве всё, что нужно для работы на паре и дома. Функции в solution.py пустые: в каждой стоит raise NotImplementedError, поэтому до начала работы все тесты падают.

lesson_08/
solution.pyпять функций: задачи 1–3 и расширение с нулями
tests.py37 публичных тестов pytest
bench.pyзамер четырёх способов найти пару, глава 8.10
initial_attempt.txtвопросы к условию и примеры, записываются до кода
complexity_note.mdзаготовка заметки: инвариант, время, память, замер
task.mdусловия задач на пару
bugs_08.pyдомашнее задание: три функции с ошибкой на границе
tests_bugs_08.pyдомашнее задание: заготовки трёх тестов
homework_08.mdусловия домашнего задания
extra.pyдополнительные задачи: LeetCode 11, 125 и 15
tests_extra.py15 тестов к дополнительным задачам

§2Порядок работы на паре

Работа идёт в папке lesson_08 на своём компьютере, нужен Python с pytest. Если Python ещё не настроен, начните с песочниц в главах 8.4, 8.7, 8.8 и 8.11: там те же функции пишутся прямо на странице и проверяются похожими тестами. Сдаётся всё равно solution.py с зелёным pytest.

  1. Скопируйте папку lesson_08 в portfolio/lesson_08/ личного репозитория и выполните python -m pytest tests.py -v. Все 37 тестов должны упасть с NotImplementedError — так проверяется, что pytest видит файлы.
  2. До кода откройте initial_attempt.txt и запишите три вопроса к условию задачи 1 и пять примеров с ответами. Образец — главы 8.3 и 8.6.
  3. Напишите pair_sum_brute. Запустите python -m pytest tests.py -k pair: тесты для перебора должны пройти, для указателей — ещё падать.
  4. Напишите pair_sum. Если какой-то тест падает, найдите его в блоках «Какие тесты ловят» главы 8.9: там показано, какая ошибка его роняет и как она выглядит в прогоне.
  5. Напишите dedup_sorted и проверьте: -k dedup. Затем compress: -k compress.
  6. Запустите замер: python bench.py > bench_results.txt. Он идёт около 20 секунд.
  7. Заполните в complexity_note.md разделы для трёх обязательных функций.
  8. Если осталось время — move_zeros, -k zeros.
  9. Если и это готово — extra.py: наибольший объём воды, палиндром с пропуском знаков, тройки с нулевой суммой. Проверка: python -m pytest tests_extra.py -v.
На полях: когда можно открыть главы

Код функций есть в главах модуля. На занятии 9 решение объясняется без подсказок, поэтому сначала пишется своя версия по правилу из главы, не открывая код. Если тест падает, откройте пошаговый прогон нужной главы и сравните шаги со своим кодом.

§3Что сдаётся с пары

Работа на паре принята, когда

Задача 1 решена двумя способамиpython -m pytest tests.py -k pair — 21 passed.
Задачи 2 и 3 решены-k dedup и -k compress без падений.
Вопросы записаны до кодаВ initial_attempt.txt три вопроса и пять примеров.
Замер сохранёнbench_results.txt с выводом bench.py на своей машине.
Решение объясняетсяДля pair_sum вы можете назвать инвариант и объяснить, почему указатели не пропускают пару, — как в главе 8.5, §6.

§4Вопросы для review

В КТП контроль занятия записан так: «Сопоставить O(n²) и O(n) с экспериментом. На review объяснить монотонность указателей, границы применимости и возможное влияние сортировки на итоговую сложность». Ниже эти требования разложены на вопросы и на то, что должно прозвучать в ответе. Главы в правом столбце — где это разобрано.

Вопрос на reviewЧто должно прозвучать в ответеГде разобрано
Как ваш замер подтверждает O(n²) у перебора и O(n) у указателей?При удвоении n время перебора растёт примерно в 4 раза, указателей — примерно в 2; назвать свои числа из bench_results.txt8.4, 8.10
Что такое монотонность указателей и почему из неё следует O(n)?Каждый указатель движется только в одну сторону и не возвращается; каждый шаг сдвигает хотя бы один, поэтому шагов не больше n8.5, §4
Когда указатели навстречу применять нельзя?Вход не отсортирован и нужны исходные индексы; пример из главы, где пара теряется на неотсортированном списке; запасной способ — словарь8.2, 8.5, 8.10
Как сортировка влияет на итоговую сложность?Если вход не отсортирован, добавляется O(n log n), и итог становится O(n log n) вместо O(n); копия списка — ещё O(n) памяти8.10, 8.11

§5Шпаргалка по модулю

Указатели навстречуi, j = 0, len(a) - 1 while i < j: s = a[i] + a[j] if s == target: return (i, j) if s < target: i += 1 else: j -= 1 return None

Нужен отсортированный вход. Каждый ход исключает один элемент. O(n), O(1).

Чтение и записьw = 0 for r in range(len(a)): if оставить(a[r]): a[w] = a[r] w += 1 del a[w:] return w

w ≤ r, запись не затирает непрочитанное. Для переноса нулей — обмен вместо записи.

Признаки в условии

«отсортирован», «пара», «сумма двух» — навстречу. «на месте», «верните длину», «подряд» — в одну сторону. «два отсортированных» — по двум спискам. «за один проход», «O(n)» — перебор исключён.

Вопросы до кода

Отсортирован ли вход и в какую сторону. Можно ли взять элемент дважды. Что вернуть, если ответа нет. Индексы или значения. Можно ли менять вход. Размер n и ограничение по памяти.

Условие цикла

i < j — когда нужна пара разных элементов (задача 1). i <= j — когда каждый элемент нужно отнести к группе (разделение). В одну сторону — for r in range(len(a)).

Сложность и замер

Перебор пар — n·(n − 1) / 2, O(n²): при удвоении n время ×4. Указатели — не больше n − 1 шагов, O(n): ×2. Словарь — O(n) и O(n) памяти. Сортировка + указатели — O(n log n).

Инвариант задачи 1

Если пара с суммой target есть, она лежит на участке с i по j. Сортировка нужна во второй проверке: a[j] — самый большой элемент участка.

Типичные ошибки

<= вместо < — пара (k, k). Перепутаны ветки — None при существующей паре. Запись вместо обмена — пропали нули. Цикл по парам соседей — пропал последний элемент.

§6Словарь модуля

ТерминЧто означаетГде введён
УказательПеременная с номером позиции в списке, значение которой меняется по правилу алгоритма8.1
Два указателяТехника прохода, в которой два индекса двигаются по данным и на каждом шаге сдвигается хотя бы один8.1
Указатели навстречуСтарт с двух концов, указатели сходятся; нужен порядок в данных8.1, 8.4
Указатели в одну сторонуОба идут слева направо: один читает, другой пишет или ищет во второй последовательности8.1, 8.7
Перебор, brute forceПроверка всех вариантов, например всех пар: n·(n − 1) / 2 проверок, O(n²)8.4
ИнвариантУтверждение, верное перед каждой проверкой условия цикла; проверяется инициализацией, сохранением и завершением8.5
Монотонность указателейУказатель движется только в одну сторону и не возвращается; отсюда оценка O(n)8.5
Изменение на местеРезультат записывается в тот же список, новый не создаётся; дополнительная память O(1)8.7
Кодирование длин серий, RLEСерия одинаковых элементов подряд записывается как элемент и число повторов8.7
ПодпоследовательностьЭлементы в исходном порядке, между которыми могут быть пропускизанятие 7, 8.7
Разделение, partitionПерестановка на месте: сначала элементы с верным условием, потом остальные8.8
Interview canvasПорядок решения на секции: условие → уточнения → примеры → перебор → оптимизация → сложность → код → проверка8.11

§7Домашнее задание

Максим Николаевич не хочет задавать домашнее задание. Но очень хочет

Коты в строительных касках склонились над чертежом, подпись: «короче надо чето делать»
ЗадачаЧто сделатьГде помогут
1 · Довести решенияsolution.py проходит все 37 тестов. bench_results.txt — ваш замер. complexity_note.md заполнен по пяти функциям, в таблице замера — рост времени при удвоении n.Главы 8.4–8.8, 8.10
2 · Три ошибки на границеВ bugs_08.py функции count_cheap_pairs, dedup_keep_two, is_mirror. Каждая верно отвечает на обычных входах и ошибается на граничном. Для каждой: вход, на котором ответ неверный; падающий тест в tests_bugs_08.py; исправление одной строки; повторный прогон. Вывод pytest — в bugs_run.txt.Глава 8.9, особенно §1 и сводка
3 · Квадраты отсортированного спискаФункция sorted_squares(nums), LeetCode 977: nums по возрастанию, есть отрицательные; вернуть новый список квадратов по возрастанию за O(n), без sorted. Пример: [-7, -3, 0, 2, 9] → [0, 4, 9, 49, 81]. Самый большой квадрат стоит на одном из концов. Не меньше пяти своих тестов: пустой список, один элемент, все отрицательные, все положительные.Главы 8.4 и 8.5: указатели навстречу

Тест к bugs_08.py засчитывается, если до исправления он падает, а после — проходит. Тест, который проходит и на ошибочной версии, ошибку не ловит.

§8Порядок сдачи

Файлыportfolio/lesson_08/: solution.py, tests.py, bench_results.txt, complexity_note.md, initial_attempt.txt, bugs_08.py, tests_bugs_08.py, bugs_run.txt
Ветка и PRВетка hw-08, коммиты с содержательными сообщениями, Pull Request в собственную main
СрокДо начала занятия 9
ПроверяемЗелёный pytest по всем файлам; три теста к bugs_08.py, которые падают до исправления; пять тестов к sorted_squares; заметка без пустых пунктов
ИИКод и тесты пишете сами: на занятии 9 решение объясняется без подсказок. Оформление .md и редактуру формулировок можно поручить модели. Я сам так делаю всегда, поэтому и вам запрещать не буду.

§9Что дальше

Перед занятием 9 задачи модуля можно прорешать в формате собеседования: в главе 8.13 восемь задач, каждая по этапам секции с секундомером.

Занятие 9 — скользящее окно фиксированного размера. Окно — это два указателя в одну сторону, между которыми всегда одно и то же расстояние k. На каждом шаге правый указатель добавляет элемент в окно, левый убирает, и сумма или счётчик окна пересчитываются за O(1). Правило «элементов n, окон длины k — n − k + 1» из занятия 7 там понадобится сразу.

Модуль 8 · Два указателя · ОП.04 · 3 курс Автор: Макаров Максим Николаевич