Существует ли конкретная структура данных, которую должен реализовать детекс в С++ STL, или это детектив, только это неопределенное понятие массива, растущего как с передней, так и с обратной стороны, для реализации, но реализация выбирает?
Я всегда полагал, что deque был круговой буфер, но я недавно читал ссылку на С++ здесь, и это звучит как deque - это какой-то массив массивов. Кажется, это не простой старый круговой буфер. Это буфер пробелов, затем или некоторый другой вариант растущего массива, или это просто зависит от реализации?
ОБНОВЛЕНИЕ И РЕЗЮМЕ ОТВЕТОВ:
Похоже, общий консенсус заключается в том, что deque - это структура данных, такая, что:
- время вставки или удаления элемента должно быть постоянным в начале или конце списка и не более линейным в другом месте. Если мы интерпретируем это как означающее истинное постоянное время и не амортизируемое постоянное время, как кто-то комментирует, это кажется сложным. Некоторые утверждают, что мы не должны интерпретировать это как неамортизированное постоянное время.
- "Deque требует, чтобы любая вставка сохраняла любую ссылку на элемент-член. Это нормально, чтобы итераторы были недействительными, но сами члены должны оставаться в одном месте в памяти." Как кто-то комментирует: это достаточно просто, просто скопировав элементы куда-нибудь в кучу и сохранив T * в структуре данных под капотом.
- "Вставка одного элемента в начале или в конце deque всегда занимает постоянное время и вызывает один вызов конструктора T." Единый конструктор T также будет достигнут, если структура данных хранит T * под капотом.
- Структура данных должна иметь произвольный доступ.
Кажется, никто не знает, как получить комбинацию 1-го и 4-го условий, если мы возьмем первое условие как "неамортизированное постоянное время". Связанный список достигает 1), но не 4), тогда как типичный круговой буфер достигает 4), но не 1). Я думаю, что у меня есть реализация, которая выполняется и ниже. Комментарии?
Начнем с реализации, которую кто-то предложил: мы выделяем массив и начинаем размещать элементы с середины, оставляя пространство как спереди, так и сзади. В этой реализации мы отслеживаем, сколько элементов из центра как в переднем, так и в обратном направлениях, вызовите эти значения F и B. Затем добавьте эту структуру данных с дополнительным массивом, который в два раза превышает размер оригинала массив (так что теперь мы теряем тонну пространства, но не изменяем асимптотическую сложность). Мы также заполним этот вспомогательный массив из его середины и дадим ему аналогичные значения F 'и B'. Стратегия такова: каждый раз, когда мы добавляем один элемент в первичный массив в заданном направлении, если F > F 'или B > B' (в зависимости от направления), до двух значений копируются из первичного массива в вспомогательный массив, пока F 'не поймает F (или B' с B). Таким образом, операция вставки включает в себя включение 1 элемента в первичный массив и копирование до 2 от первичного к вспомогательному, но оно все еще O (1). Когда первичный массив заполняется, мы освобождаем первичный массив, делаем вспомогательный массив основным массивом и делаем еще один вспомогательный массив, который еще в 2 раза больше. Этот новый вспомогательный массив начинается с F '= B' = 0 и не имеет ничего скопированного с ним (поэтому параметр resize op равен O (1), если распределение кучи является сложностью O (1)). Так как вспомогательные копии 2 элементов для каждого элемента, добавленного в первичный и первичный, запускаются не более половины, невозможно, чтобы вспомогательный элемент не догнал первичный к моменту, когда первичный пробег снова исчезнет. Удаление также просто нужно удалить 1 элемент из первичного и 0 или 1 из вспомогательного. Таким образом, если предположить, что распределение кучи O (1), эта реализация выполняет условие 1). Мы делаем массив из T * и используем new
всякий раз, когда вставляем для выполнения условий 2) и 3). Наконец, 4) выполняется, потому что мы используем структуру массива и можем легко реализовать доступ O (1).