Ответ
Операция append (добавление в конец) для динамического массива имеет амортизированную сложность O(1), но в наихудшем случае (worst-case) её сложность равна O(n).
Причина: Когда при добавлении нового элемента текущая вместимость (capacity) массива исчерпана, происходит реаллокация:
- Выделяется новый, больший блок памяти (обычно capacity увеличивается в 1.5-2 раза).
- Все существующие
nэлементов копируются в новый блок. - Старый блок памяти освобождается.
Этот процесс копирования и делает единичную операцию в этот момент O(n).
Пример на Swift:
var array = [Int]()
// Предположим, начальная capacity = 0
array.append(1) // Реаллокация: capacity -> 1
array.append(2) // Реаллокация: capacity -> 2
array.append(3) // Реаллокация: capacity -> 4
// Копируются 2 элемента. Следующие 3 операции append будут O(1).
Оптимизация: Чтобы избежать частых реаллокаций при известном ожидаемом размере, используйте reserveCapacity(_:).
array.reserveCapacity(1000) // Одна реаллокация под 1000 элементов.
for i in 0..<1000 {
array.append(i) // Все 1000 операций будут O(1).
} Ответ 18+ 🔞
А, слушай, про эту вашу операцию append в динамических массивах. Ну, типа, в среднем она, конечно, быстрая, O(1), все дела. Но иногда, блядь, случается такое, что она превращается в настоящего пиздец-монстра, который жрёт O(n) времени. Почему? А вот почему, ёпта!
Представь себе, ты — массив. У тебя есть квартирка, capacity. Ты в ней живёшь, элементы свои хранишь. И вот ты потихоньку заполняешься, как бочка под пиво в пятницу. И в один прекрасный момент — хоп! — места больше нет, а тебе надо ещё один элемент впихнуть.
И что происходит? А происходит ебаный переезд, блядь! Реаллокация!
- Ты ищешь новую, большую квартиру (память выделяется, обычно в 1.5-2 раза больше).
- Ты, сука, начинаешь таскать всю свою старую хуйню — все
nэлементов — в это новое жилище. Копируешь, блядь, каждый стул, каждую табуретку. - Старую квартиру (память) освобождаешь, чтоб не платить за две.
Вот этот процесс перетаскивания всей своей ебаной жизни и делает одну-единственную операцию append в этот конкретный момент O(n). Не каждый раз, а именно когда крыша едет и надо расширяться.
Смотри, как это на Swift выглядит, чтоб совсем понятно было:
var array = [Int]() // Пустой массив, жилплощадь = 0
array.append(1) // Бля, негде жить! Реаллокация! capacity -> 1
array.append(2) // Опять негде! Реаллокация! capacity -> 2
array.append(3) // Снова, мать его, тесно! Реаллокация! capacity -> 4, копируем 2 старых элемента.
// А вот теперь, сука, раздолье! Следующие три добавления будут быстрыми, O(1).
Как с этим бороться, чтоб не бздеть перед каждым append?
Есть же способ, ядрёна вошь! Если ты заранее знаешь, что народу будет дохуя (ну, 1000 элементов, например), так скажи об этом массиву! Скажи: «Резервируй, блядь, место!».
array.reserveCapacity(1000) // Один раз проебёшься с реаллокацией на 1000 мест.
for i in 0..<1000 {
array.append(i) // И все 1000 раз будешь просто класть элемент на полку, O(1), без нервов и переездов.
}
Вот и вся магия. В среднем — быстро, в худшем случае — пиздец, но если головой думать, то можно этот пиздец предупредить.