August 27

10 паттернов, которые решают 80% лайвкодинга

На лайвкодинге задачи звучат по-разному, но решаются несколькими повторяющимися приёмами. Ваша цель — распознать сюжет задачи и быстро выбрать паттерн. У каждого раздела ниже есть:

  • Как понять, что это ваш кейс (ключевые слова из условия),
  • Алгоритм в 3–5 шагов,
  • Код (минимальный, понятный),
  • Ошибки и подсказки,
  • Сложность.

1) Словарь и collections.Counter

Когда это ваш паттерн

  • В условии есть «посчитать/частота/сколько раз», «сопоставить ключ → значение», «кэшировать результат».
  • Нужен быстрый доступ по ключу.

Алгоритм

  1. Выберите ключ (что считаем/по чём группируем).
  2. Пройдитесь по данным, обновляя словарь/Counter.
  3. При необходимости — возьмите 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?», «пересечение/разница/объединение».
  • Нужно быстро проверять «видел/не видел».

Алгоритм

  1. Превратите коллекцию в set, чтобы убрать дубли.
  2. Используйте & | - ^ для операций между множествами.
  3. Для проверки присутствия — 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», «максимальная/минимальная сумма/счётчик на отрезке», «за один проход».

Алгоритм (фиксированное окно)

  1. Посчитайте метрику для первых k элементов.
  2. Двигайте окно вправо: вычтите ушедший слева, добавьте пришедший справа.
  3. Обновляйте лучший ответ.

Код

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 разных символов» — расширяете окно вправо, при нарушении условия сдвигаете левую границу.

Сложность: O(n).


4) Два указателя (Two Pointers)

Когда это ваш паттерн

  • Дано отсортировано: «найдите пару с суммой target», «минимальная разница», «слияние отсортированных», «идти с концов».

Алгоритм

  1. Поставьте указатели i (слева) и j (справа).
  2. Сравните текущую комбинацию с целевым условием.
  3. Двигайте один из указателей так, чтобы приблизиться к цели.
  4. Повторяйте, пока 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)

Ошибки и подсказки

  • Если массив не отсортирован — либо сортируем (меняется задача: индексы!), либо берём другой подход (хеш-таблица).
  • Для «удалить дубли на месте» в отсортированном — тоже два указателя.

Сложность: O(n).


5) Стек

Когда это ваш паттерн

  • «Парные элементы/скобки», «откаты/undo», «удалять соседние одинаковые», «нужен обратный порядок последнего добавленного».

Алгоритм (на примере разворота слов)

  1. Кладите элементы по мере чтения в стек.
  2. Доставайте (pop) назад — получите обратный порядок.

Код

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) Парсинг строк и логов

Когда это ваш паттерн

  • «Строка известного формата», «разделитель фиксирован», «вытащить level/date/message», «по шаблону».

Алгоритм

  1. Попробуйте split с maxsplit (чтобы не резать «хвост» сообщения).
  2. Если формат сложный/шумный — переходите к re (регэкспы).
  3. Валидируйте, что нужные части есть.
  4. Если нужно что-то заменить используйте 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)

  1. Создайте defaultdict(list).
  2. Пройдитесь по данным, вычисляя ключ группы.
  3. Кладите элемент в 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 требует предварительно отсортировать по ключу.

Сложность: O(n).


8) Top-K (куча heapq или сортировка)

Когда это ваш паттерн

  • «Найдите k самых больших/маленьких», «топ-10 частых», «поток данных», «нет смысла сортировать всё».

Алгоритм

  1. Если n маленькое — отсортируйте и возьмите срез.
  2. Если данные большие или поток — используйте кучу (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) Дедупликация с сохранением порядка

Когда это ваш паттерн

  • «Удалите дубликаты, сохранив порядок первого появления».

Алгоритм

  1. Держите seen = set().
  2. Идите по элементам: если не в seen — добавьте в результат и в seen.
  3. Верните результат.

Код

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]

Ошибки и подсказки

  • set(seq) сломает порядок. Этот приём — безопасный для порядка.
  • Элементы должны быть хешируемыми.

Сложность: O(n).


10) Валидация скобок (стековый разбор)

Когда это ваш паттерн

  • «Правильная скобочная последовательность», «парные теги/символы», «корректная вложенность».

Алгоритм

  1. Идёте слева направо.
  2. Открывающие — кладёте в стек.
  3. На закрывающую — стек не пуст и верх совпадает с парой → снимаете; иначе ошибка.
  4. В конце стек должен быть пуст.

Код

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

Подсказка: легко расширяется под другие парные символы.

Сложность: O(n).


Быстрый «детектор» по формулировке задачи

  • Посчитать/частоты/сопоставить → 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!) — экспонента/факториал.Перебор всех подмножеств/перестановок. Быстро становится неподъёмно.Пример: полный перебор вариантов в задачах на комбинаторику.

Короткий вывод

  1. Узнавайте сюжет задачи по ключевым словам.
  2. Примеряйте паттерн из списка выше.
  3. Пишите минимальное решение (один проход, простая структура данных).
  4. Добавляйте проверки краёв (пустой ввод, k вне диапазона, неотсортированный массив и т.д.).

Обязательно ли идти в автоматизацию, чтобы остаться востребованным кадром?

На этот и другие вопросы ответил в большой подробной статье https://teletype.in/@rvtsakunov/ZbddeSHnNsr