Big O: время и память

Big O описывает верхнюю асимптотическую границу роста затрат при росте размера входа , отбрасывая постоянные множители и младшие члены. Обычно отдельно говорят о времени и дополнительной памяти: быстрый поиск в hash map может требовать памяти, а сортировка на месте — менять входной массив.

Зачем это на интервью

Нужно уметь выбрать структуру данных под операцию и честно назвать её цену. Интервьюер ожидает не только «», но и условия: средний или худший случай, что считается входом, какая память занята и как решение ведёт себя при реальных данных.

Минимум для E4

  • Определять и считать вложенные проходы, а не количество строк кода.
  • Различать , , , и .
  • Указывать дополнительную память отдельно от памяти входа.
  • Называть средний и худший случай, если они различаются.

Один цикл по массиву — ; два последовательных цикла также , потому что асимптотически растёт как . Два вложенных прохода по независимым диапазонам дают произведение: , а при — . Бинарный поиск требует отсортированного массива и делает сравнений.

Углубление для E5/Senior

Асимптотика — модель, а не latency budget. Для E5 важно связать её с распределением входов и ресурсами: hash table даёт ожидаемое при корректном распределении хешей, но resize и коллизии меняют наблюдаемую стоимость; сортировка может быть , но лишняя копия увеличит peak memory и давление на GC. При выборе API также считают стоимость на границе: сериализация, сетевой round trip и N+1 запросов способны доминировать над сложностью локального алгоритма.

Сложность описывают для конкретной операции. map не делает весь сервис : построение индекса, обработка каждого ключа и объём данных сохраняют зависимость от . Для неизвестного масштаба полезно записать модель, затем проверить её benchmark-ом на малом, типичном и предельном входе.

Ключевые понятия

  • Входной размер — параметр, от которого растут затраты; для графа часто нужны вершин и рёбер.
  • Временная сложность — число базовых операций в модели; это не точные наносекунды.
  • Дополнительная память — память сверх входа и результата, если явно не оговорено другое.
  • Худший случай — верхняя граница на всех допустимых входах; средний/ожидаемый требует модели распределения или вероятности.
  • In-place — алгоритм использует ограниченную дополнительную память, но может изменять входные данные.
  • Асимптотическая нотация — задаёт верхнюю границу; — тесную границу порядка роста, когда она доказана.

Типовые вопросы

  1. Почему два последовательных цикла — не ?
    • Они выполняют примерно шагов; постоянный множитель отбрасывается, поэтому .
  2. Какова сложность поиска в hash map?
    • Обычно ожидаемое на операцию, но это зависит от хеширования и коэффициента заполнения; худший случай может быть хуже.
  3. Почему binary search не всегда подходит?
    • Он требует отсортированной коллекции и случайного доступа; надо учесть стоимость сортировки и обновлений.
  4. Что значит ?
    • Есть два независимых размера: например, перебор пользователей и прав каждого. Нельзя без основания заменить на .
  5. Может ли быть приемлемым?
    • Да, при малом ограниченном или редкой фоновой операции; решение обосновывают лимитом, измерением и budget-ом.
  6. Почему не означает мгновенно?
    • Константа может включать кэш-промах, аллокацию, lock или системный вызов; асимптотика не измеряет их.

Практика

  • Оцените время и дополнительную память для deduplication массива из строк через вложенный поиск и через map[string]struct{}. Готово: для обоих вариантов указаны средний и худший случай, судьба порядка элементов и используемая дополнительная память.
  • Реализуйте поиск пары с заданной суммой: квадратичный проход и вариант с hash map. Готово: тесты покрывают пустой, повторяющийся и большой вход; benchmark сравнивает одинаковые данные и показывает ns/op, B/op, allocs/op.
  • Разберите путь списка ID в SQL. Готово: обозначены размер списка, число round trip, риск N+1 и хотя бы один способ измерить реальную стоимость.

Частые ошибки и ловушки

  • Называть сложность без определения и без различения среднего и худшего случая.
  • Забывать память на копию результата, индекс или рекурсивный стек.
  • Считать, что вложенный цикл всегда : диапазоны могут быть разными или внутренний указатель двигаться монотонно.
  • Оптимизировать локальный код, когда узким местом является сеть или база данных.
  • Сравнивать benchmark-ы на разных входах либо без защиты результата от оптимизации компилятором.

Связанные темы

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

Источники

  • Thomas H. Cormen et al., Introduction to Algorithms, главы об асимптотическом анализе.
  • Robert Sedgewick and Kevin Wayne, Algorithms, разделы об анализе алгоритмов.
  • Go project, The Go Programming Language Specification, разделы о map и slice.
  • Go project, документация пакета testing, раздел Benchmarks.