Вступить в клуб →
средняявопросСложность и стоимость структур данных

Почему n вызовов append дают линейное, а не квадратичное время

Список в CPython — динамический массив: элементы лежат в непрерывном буфере, и когда место заканчивается, append выделяет новый буфер и копирует туда всё накопленное содержимое.

out = []
for x in data:      # n элементов
    out.append(x)

Казалось бы, копирование внутри цикла обязано дать квадрат. Почему на самом деле весь цикл отрабатывает за O(n)?

  • Копирования нет вовсе: список в CPython реализован как связный, и `append` просто цепляет новый узел к хвосту, не трогая уже добавленные элементы
  • Копирование есть, но выполняется кодом на уровне C, а операции, реализованные в C, в асимптотику не входят: считаются только витки Python-цикла
  • Интерпретатор заранее вычисляет длину `data` и выделяет буфер нужного размера ещё до начала цикла, поэтому расширений не происходит ни разу
  • Буфер расширяется не на один элемент, а с запасом, пропорциональным текущему размеру: расширений выходит порядка log n, а суммарно копируется порядка n элементов — амортизированно O(1) на вызов

🔒 Проверка ответа — для участников клуба

  • Проверка ответа
  • Подсказка, если застряли
  • Разбор с объяснением, почему так
  • Прогресс по всем задачам и виртуальные собеседования
Зарегистрироваться →

Регистрация занимает минуту

Другие задачи раздела