Требования к сложности для std:: deque:: push_back/front

В результате этого вопроса из нескольких дней назад есть несколько вещей, которые подталкивали меня к требованиям сложности для std::deque::push_back/push_front против фактического std::deque реализация в дикой природе.

Результат предыдущего вопроса состоял в том, что для этих операций требуется O(1) наихудшая сложность. Я подтвердил, что это действительно так в c++11:

из 23.3.3.4 модификаторы deque, ссылающиеся на вставку, push/emplace front/back

Сложность: сложность линейна по числу вставленных элементов плюс меньше расстояний до начала и конца дека. Вставка одного элемент либо в начале, либо в конце дека всегда принимает постоянное время и вызывает один вызов конструктора T.

Это сочетается с требованием сложности O(1) для индексирования через operator[] и т.д.

Проблема заключается в том, что реализации не полностью удовлетворяют этим требованиям.

В терминах msvc и gcc реализация std::deque представляет собой заблокированную структуру данных, состоящую из блоков динамических массивов указателей на (фиксированный размер), где каждый блок хранит несколько элементов данных.

В худшем случае push_back/front etc может потребоваться выделение дополнительного блока (это ограничение - фиксированное распределение по размеру равно O(1)), но также может быть изменено размер динамического массива указателей блоков - это не так, так как это O(m), где m - количество блоков, которое в конце дня O(n).

Очевидно, это по-прежнему амортизируется сложностью O(1) и, поскольку обычно m << n на практике это будет довольно быстро. Но, похоже, проблема с соответствием?

В качестве еще одного момента я не вижу, как вы можете создать структуру данных, которая строго удовлетворяет как сложности O(1) для push_back/front etc, так и operator[]. У вас может быть связанный список указателей блоков, но это не дает вам желаемого поведения operator[]. Можно ли это сделать?

Ответ 1

В С++ 11 FDIS мы можем прочитать:

23.2.3 Контейнеры последовательности [sequence.reqmts]

16/ В таблице 101 перечислены операции, которые предоставляются для некоторых типов контейнеров последовательностей, но не для других. Реализация должна обеспечивать эти операции для всех типов контейнеров, указанных в столбце "контейнер", и выполнять их таким образом, чтобы принимать амортизированное постоянное время.

Здесь таблица 101 называется необязательными операциями контейнера последовательности и списками deque для операций push_back и push_front.

Следовательно, это похоже на небольшое упущение в указанном вами параграфе. Возможно, стоит отчет о дефектах?

Обратите внимание, что одиночный вызов конструктора все еще сохраняется.

Ответ 2

Я подозреваю, что перераспределение указателей блоков выполняется с геометрически увеличивающимся размером - это общий трюк для std::vector. Я думаю, что это технически O (log m), но поскольку вы указываете m < n, так как практический вопрос не влияет на реальные результаты.