1. Продвинутые структуры данных и алгоритмы в Python
Продвинутые структуры данных и алгоритмы в Python
Почему на собеседовании вас просят реализовать стек на списках, а не просто сказать «я знаю, что это такое»? Потому что знание структур данных — это не зубрёжка определений, а умение выбирать правильный инструмент под задачу. Ошибка в выборе структуры данных превращает быстрый код в тормозящий монстр. А интервьюер именно это и проверяет: понимаете ли вы, почему используете dict, а не list.
Сложность операций: единственная таблица, которую нужно знать наизусть
Прежде чем разбирать конкретные структуры, зафиксируем базу — асимптотическую сложность операций. На собеседовании вас спросят: «Какова сложность поиска элемента в set?» — и ждут ответа в среднем случае.
| Структура | Доступ по индексу | Поиск элемента | Вставка | Удаление |
|-----------|-------------------|----------------|---------|----------|
| list | | | в середину, в конец | |
| dict | по ключу | по ключу | | |
| set | — | | | |
| deque | | | с обоих концов | с обоих концов |
> Главное правило: если вам нужен быстрый поиск по значению — set или dict. Если важен порядок и доступ по индексу — list. Если нужны вставки и удаления с обоих концов — deque.
Стек и очередь: когда list — плохой выбор
Стек — это структура «последний вошёл — первый вышел» (LIFO). Реализация на list проста:
Очередь — «первый вошёл — первый вышел» (FIFO). Если реализовать её на list, удаление из начала будет , потому что все элементы сдвигаются. Именно поэтому для очереди используют collections.deque:
На собеседовании часто просят реализовать стек с получением минимума за . Идея: хранить второй стек, в котором на каждом уровне — текущий минимум:
Словари: defaultdict, Counter и хитрости подсчёта
Самая частая задача на собеседовании, связанная со словарями, — подсчёт элементов. Без Counter вы пишете:
С Counter — одну строку:
Counter умеет больше: most_common(n) вернёт самых частых элементов, а арифметика между Counter работает как с мультимножествами:
defaultdict избавляет от проверки наличия ключа. Типичный кейс — группировка элементов:
Без defaultdict пришлось бы писать if department not in groups: groups[department] = [] — лишний код, лишние ошибки.
Куча и приоритетная очередь
Куча (heap) — структура данных, которая за возвращает минимальный элемент и за выполняет вставку и удаление. В Python это модуль heapq, работающий с обычным list:
> Python реализует min-heap. Если нужна max-heap — вставляйте отрицательные значения: heapq.heappush(heap, -value).
Классическая задача: найти наибольших элементов. В лоб — сортировка за . С кучей — , что выгодно при малом :
На собеседовании часто просят реализовать слияние отсортированных списков. Решение — куча размером :
Хэш-таблицы и задачи на Two Sum
Хэш-таблица — это то, как dict и set устроены внутри. Знание этого принципа критично, потому что целый класс задач решается именно через «жертвуем памятью ради скорости».
Классика — Two Sum: найти два числа в массиве, сумма которых равна целевой. Перебор всех пар — . Через dict — :
Этот паттерн — «пройти один раз, запомнить в dict» — встречается в десятках задач: Contains Duplicate, Group Anagrams, Longest Consecutive Sequence.
Сортировка и поиск: встроенные инструменты
Python даёт sorted() и list.sort(). Разница: sorted() возвращает новый список, sort() сортирует на месте. Оба используют Timsort — гибрид сортировки слиянием и вставками со сложностью .
Ключевой параметр — key:
Бинарный поиск — на отсортированных данных. В Python — bisect:
На собеседовании бинарный поиск часто просят реализовать вручную — и это тот случай, где важно не сбиться с границами (left, right, mid):
Графы: обходы в ширину и глубину
Графы на собеседовании встречаются реже, но если встречаются — это обычно BFS (поиск в ширину) или DFS (поиск в глубину). Представление графа — словарь смежности:
BFS использует очередь и находит кратчайший путь в невзвешенном графе:
DFS использует стек (или рекурсию) и полезен для задач на связность, топологическую сортировку, поиск циклов:
> На собеседовании всегда уточняйте: «Граф взвешенный или нет? Ориентированный? Есть ли циклы?» — это показывает системное мышление.
Что запомнить
Каждая структура данных — это компромисс между скоростью и памятью. dict и set дают за счёт дополнительной памяти. deque даёт на обоих концах, но на доступ по индексу. Куча даёт быстрый доступ к минимуму, но не к произвольному элементу. Задача интервьюера — убедиться, что вы понимаете эти компромиссы, а не просто знаете API.