10 паттернов, которые решают 80% лайвкодинга
На лайвкодинге задачи звучат по-разному, но решаются несколькими повторяющимися приёмами. Ваша цель — распознать сюжет задачи и быстро выбрать паттерн. У каждого раздела ниже есть:
- Как понять, что это ваш кейс (ключевые слова из условия),
- Алгоритм в 3–5 шагов,
- Код (минимальный, понятный),
- Ошибки и подсказки,
- Сложность.
1) Словарь и collections.Counter
- В условии есть «посчитать/частота/сколько раз», «сопоставить ключ → значение», «кэшировать результат».
- Нужен быстрый доступ по ключу.
- Выберите ключ (что считаем/по чём группируем).
- Пройдитесь по данным, обновляя словарь/Counter.
- При необходимости — возьмите most_common(k) или обращайтесь как к обычному dict.
from collections import Counter fruits = ["apple", "banana", "apple", "orange", "banana", "apple"] counts = Counter(fruits) print(counts) # Counter({'apple': 3, 'banana': 2, 'orange': 1}) print(counts["apple"]) # 3 # Топ-2 print(counts.most_common(2)) # [('apple', 3), ('banana', 2)]
- Ключ должен быть хешируемым (строка, число, кортеж). Списки/словари — нельзя.
- Не ловите KeyError: используйте get(key, 0) или defaultdict.
Сложность: операции по ключу — ~O(1); один проход — O(n).
2) Множество (set)
- «Оставьте уникальные», «есть ли X?», «пересечение/разница/объединение».
- Нужно быстро проверять «видел/не видел».
- Превратите коллекцию в set, чтобы убрать дубли.
- Используйте & | - ^ для операций между множествами.
- Для проверки присутствия — x in seen.
a = [1, 2, 2, 3] b = [2, 3, 4] common = set(a) & set(b) print(sorted(common)) # [2, 3]
- set не сохраняет порядок. Если порядок важен — см. «Дедуп» ниже.
- Множество подходит и как «быстрый фильтр»: пропускаем только «не виденные».
Сложность: add, in, remove — ~O(1); преобразование — O(n).
3) Скользящее окно (Sliding Window)
- «Подмассив/подстрока длины k», «максимальная/минимальная сумма/счётчик на отрезке», «за один проход».
- Посчитайте метрику для первых k элементов.
- Двигайте окно вправо: вычтите ушедший слева, добавьте пришедший справа.
- Обновляйте лучший ответ.
def max_sum_window(nums, k): if k <= 0 or k > len(nums): return 0 win = sum(nums[:k]) best = win for i in range(k, len(nums)): win += nums[i] - nums[i - k] # убрали слева, добавили справа best = max(best, win) return best print(max_sum_window([5, 1, 3, 8, 2, 5, 9], 4)) # 24 (8+2+5+9)
Подсказка (переменное окно): «самая длинная подстрока без повторов», «не более k разных символов» — расширяете окно вправо, при нарушении условия сдвигаете левую границу.
4) Два указателя (Two Pointers)
- Дано отсортировано: «найдите пару с суммой target», «минимальная разница», «слияние отсортированных», «идти с концов».
- Поставьте указатели i (слева) и j (справа).
- Сравните текущую комбинацию с целевым условием.
- Двигайте один из указателей так, чтобы приблизиться к цели.
- Повторяйте, пока i < j.
def two_sum_sorted(nums, target): i, j = 0, len(nums) - 1 while i < j: s = nums[i] + nums[j] if s == target: return i, j if s < target: i += 1 else: j -= 1 return None print(two_sum_sorted([2, 7, 11, 15], 9)) # (0, 1)
- Если массив не отсортирован — либо сортируем (меняется задача: индексы!), либо берём другой подход (хеш-таблица).
- Для «удалить дубли на месте» в отсортированном — тоже два указателя.
5) Стек
- «Парные элементы/скобки», «откаты/undo», «удалять соседние одинаковые», «нужен обратный порядок последнего добавленного».
Алгоритм (на примере разворота слов)
def reverse_words(sentence: str) -> str: stack = [] for w in sentence.split(): stack.append(w) out = [] while stack: out.append(stack.pop()) return " ".join(out) print(reverse_words("Live code example")) # "example code Live"
Подсказка: «встретили пару — удалите» (например, соседние дубликаты) — идеальная история для стека.
Сложность: операции append/pop — O(1); проход — O(n).
6) Парсинг строк и логов
- Попробуйте split с maxsplit (чтобы не резать «хвост» сообщения).
- Если формат сложный/шумный — переходите к re (регэкспы).
- Валидируйте, что нужные части есть.
- Если нужно что-то заменить используйте replace у String
line = "ERROR 2021-08-27 Connection failed: Timeout" level, date, message = line.split(" ", maxsplit=2) print(level) # ERROR print(date) # 2021-08-27 print(message) # Connection failed: Timeout
- Без maxsplit легко «порезать» текст сообщения на лишние куски.
- Для CSV лучше использовать модуль csv.
Сложность: O(n) по длине строки.
7) Группировка (defaultdict, groupby)
- Создайте defaultdict(list).
- Пройдитесь по данным, вычисляя ключ группы.
- Кладите элемент в groups[key].
from collections import defaultdict words = ["apple", "ant", "banana", "carrot", "cherry"] groups = defaultdict(list) for w in words: groups[w[0]].append(w) print(dict(groups)) # {'a': ['apple', 'ant'], 'b': ['banana'], 'c': ['carrot', 'cherry']}
Подсказка: itertools.groupby требует предварительно отсортировать по ключу.
8) Top-K (куча heapq или сортировка)
- Если n маленькое — отсортируйте и возьмите срез.
- Если данные большие или поток — используйте кучу (heapq.nlargest/nsmallest) или поддерживайте мин-кучуразмера k.
import heapq Pyt numbers = [5, 1, 3, 8, 2, 5, 9, 10] print(heapq.nlargest(3, numbers)) # [10, 9, 8] print(heapq.nsmallest(3, numbers)) # [1, 2, 3]
Подсказка: для «топ-k самых частых слов» удобно: Counter(text).most_common(k).
Сложность: ~O(n log k); полная сортировка — O(n log n).
9) Дедупликация с сохранением порядка
- Держите seen = set().
- Идите по элементам: если не в seen — добавьте в результат и в seen.
- Верните результат.
def dedup_keep_order(seq): seen = set() out = [] for x in seq: if x not in seen: seen.add(x) out.append(x) return out print(dedup_keep_order([3, 1, 2, 3, 1, 4, 2])) # [3, 1, 2, 4]
10) Валидация скобок (стековый разбор)
- Идёте слева направо.
- Открывающие — кладёте в стек.
- На закрывающую — стек не пуст и верх совпадает с парой → снимаете; иначе ошибка.
- В конце стек должен быть пуст.
def is_valid_brackets(s: str) -> bool: pairs = {')': '(', ']': '[', '}': '{'} stack = [] for ch in s: if ch in '([{': stack.append(ch) elif ch in ')]}': if not stack or stack[-1] != pairs[ch]: return False stack.pop() return not stack print(is_valid_brackets("([])")) # True print(is_valid_brackets("([)]")) # False
Подсказка: легко расширяется под другие парные символы.
Быстрый «детектор» по формулировке задачи
- Посчитать/частоты/сопоставить → dict/Counter.
- Уникальные/пересечение/проверка наличия → set.
- Подмассив/подстрока длины k, максимум/минимум на окне → скользящее окно.
- Отсортировано + пара/разница/слияние → два указателя.
- Парные конструкции/обратный порядок/undo → стек.
- Строка известного формата → split/re.
- Сгруппировать по признаку → defaultdict/groupby.
- Нужны только top-k → heapq/most_common.
- Убрать дубли и сохранить порядок → дедуп с seen.
- Проверить корректность скобок → стек.
Что вообще такое сложность
- Временная — сколько шагов примерно сделает ваш алгоритм, когда вход (n) растёт.
- Памяти — сколько дополнительного места ему нужно (кроме самих данных).
n — это размер входа: длина списка, число строк в логе, количество вершин в графе и т.д.
Самые частые «уровни скорости»
- O(1) — константная.Время почти не меняется при росте данных.Пример: проверить, есть ли ключ в dict/set.
- O(log n) — логарифмическая.Каждый шаг «делим пополам».Пример: бинарный поиск по отсортированному массиву.
- O(n) — линейная.Один ровный проход по данным.Примеры: скользящее окно, два указателя, подсчёт через Counter.
- O(n log n) — почти линейная (но медленнее).Чаще всего — сортировка.Пример: отсортировать список, взять топ-k через сортировку.
- O(n²) — квадратичная.Почти всегда два вложенных цикла «каждый с каждым».Пример: наивно искать дубликаты двойным циклом.
- O(2^n), O(n!) — экспонента/факториал.Перебор всех подмножеств/перестановок. Быстро становится неподъёмно.Пример: полный перебор вариантов в задачах на комбинаторику.
Короткий вывод
- Узнавайте сюжет задачи по ключевым словам.
- Примеряйте паттерн из списка выше.
- Пишите минимальное решение (один проход, простая структура данных).
- Добавляйте проверки краёв (пустой ввод, k вне диапазона, неотсортированный массив и т.д.).
Обязательно ли идти в автоматизацию, чтобы остаться востребованным кадром?
На этот и другие вопросы ответил в большой подробной статье https://teletype.in/@rvtsakunov/ZbddeSHnNsr