Алгоритмы с нуля
Инструментарий: распознавание паттернов в коде
На собеседовании или код-ревью тебе редко дают помеченную задачу — тебе дают фрагмент и ограничение. Навык в том, чтобы прочесть цикл и назвать технику, которую он реализует, или заметить, что выбранная структура неверна. Прочти каждый набросок, затем ответь.
Потренируй распознавательную половину инструментария: по коду определи паттерн, оцени, подходит ли структура данных доминирующей операции, и выбери изменение, которое реально сдвигает сложность.
Фрагмент 1 — вложенный цикл по отсортированным данным
def has_pair_with_sum(a, target): # a is sorted ascending
n = len(a)
for i in range(n):
for j in range(i + 1, n):
if a[i] + a[j] == target:
return True
return False
Вход отсортирован, но это O(n^2). К какому паттерну зовёт отсортированный вход и какова итоговая сложность?
Фрагмент 2 — рекурсия, которая пересчитывает
def ways(n, coins):
if n == 0: return 1
if n < 0 or not coins: return 0
# use coins[0] again, or drop it
return ways(n - coins[0], coins) + ways(n, coins[1:])
Это корректно, но экспоненциально при большом n. Каков точный диагноз и стандартная правка?
Фрагмент 3 — текущий k-й элемент
import heapq
def stream_kth_largest(stream, k):
h = [] # what kind of heap?
for x in stream:
heapq.heappush(h, x) # Python heapq is a MIN-heap
if len(h) > k:
heapq.heappop(h) # pops the smallest
return h[0] # the k-th largest seen so far
Этот код держит текущий k-й наибольший через min-heap размера k. Почему именно MIN-heap (а не max-heap) — верная структура здесь?
Фрагмент 4 — неограниченное окно
def longest_unique_substring(s):
seen = set()
best = 0
for r in range(len(s)):
while s[r] in seen:
seen.add(s[r]) # BUG: should shrink from the left
seen.add(s[r])
best = max(best, len(seen))
return best
Задумана техника скользящего окна по строке. Окно никогда не сжимается, поэтому цикл сломан. Каков верный механизм?
Чтение кода ради паттерна — тот же поток, прогнанный в обратную сторону: вложенный цикл по отсортированным данным должен быть two pointers (O(n)); рекурсия, пересчитывающая одинаковые состояния, кричит о мемоизации или табуляции (DP); текущий k-й элемент задаёт конкретную ориентацию кучи тем, какой элемент нужно дёшево вытеснять; а ‘длиннейший непрерывный диапазон при условии’ — это скользящее окно, чей левый указатель движется только вперёд. Назови паттерн, сверь структуру данных с доминирующей операцией, затем измени то единственное, что сдвигает сложность.