Сложность удаления элементов в векторах и deque
Я читал, что сложность добавления элементов в конец std::vector
амортизируется с постоянной и вставки элементов в верхней и нижней части std::deque
постоянна.Поскольку оба этих контейнера имеют итератор произвольного доступа, доступ к элементам с любым индексом является постоянным. Пожалуйста, дайте мне знать,если я ошибаюсь.Мой вопрос: если доступ к элементу в std::vector
или std::deque
является константой, тогда почему временная сложность удаления элемента через стирание O(n). Один из ответы здесь здесь говорится, что удаление элементов через erase равно O (n). Я знаю, что erase удаляет элементы между начальными итераторами и конечным, поэтому ответ в основном означает, что его O(n)
в зависимости от отсутствия элементов между двумя итераторами и того, что удаление одного элемента из вектора / deque в любом индексе будет равно нулю?
3 ответов
вещи немного отличаются для std::vector
и std::deque
, а также они отличаются для C++98 и C++11.
std:: vector
сложность std::vector::erase()
линейно как по длине стираемого диапазона, так и по количеству элементов между концом диапазона и концом контейнера (поэтому стирание элемента с конца занимает постоянное время).
C++2003 [lib.vector.modifiers]
гласит:
iterator erase(iterator position);
iterator erase(iterator first, iterator last);`
...
сложность: деструктор
T
называется число раз, равное количеству элементов стер, но ... --29-->оператор присваивания ofT
называется число раз, равное числу элементов в векторе после стираемых элементов.
C++14 проект N4140 [vector.modifiers]
гласит:
сложность: деструктор
T
называется число раз, равное количество элементов стерто, но двигаться оператор присваивания ofT
называется число раз, равное количеству элементы в векторе после стираемых элементов.
таким образом, вы видите, что реализация c++11/14 в целом более эффективна, поскольку она выполняет назначение перемещения вместо назначения копирования, но сложность остается той же.
std:: deque
сложность std::deque::erase()
линейно как к длина диапазона стерта и до минимум из двух чисел: количество оставшихся элементов до начала серии, и количество оставшихся элементов после окончания серии. Таким образом, стирание элемента либо с начала, либо с конца занимает постоянное время.
C++2003 [lib.deque.modifiers]
:
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
сложность: количество вызовов деструктора такое же, как и количество стираемых элементов, но число вызов оператора присваивания имеет вид максимум равный минимуму количества элементов до стираемых элементов и количество элементов после стираемых элементов.
C++14 проект N4140 [deque.modifiers]/5
:
сложность: число вызовов деструктора совпадает с числом стираемых элементов, но число вызовов оператора присваивания равно не больше чем меньше количество элементов до стираемых элементов и количество элементов после стираемых элементов.
так вот, то же самое и в C++98 и C++11/14, опять же за исключением того, что c++11 может выбрать между перемещение назначения и скопировать задание (здесь я вижу некоторые несоответствия в стандартной, потому что слова не говоря уже о мове задание для std::vector
- может быть причиной другой вопрос).
обратите внимание также на "не более "и" не более " в формулировках. Этот позволяет реализациям быть более эффективными, чем линейные, хотя на практике они линейны (демо).
стирание элемента в векторе равно O (n), так как после удаления элемента вам все равно нужно переместить все последовательные элементы, чтобы заполнить созданный пробел. Если вектор имеет n элементов, то в худшем случае вам нужно будет сдвинуть n-1 элементов, следовательно, сложность O(n).
удаление элементов действительно O(n)
не из-за того, что вы должны сделать, чтобы найти элемент для удаления, но из-за того, что вы должны сделать со всеми из них после его. Эти элементы должны быть сдвинуты вниз, чтобы заполнить пустой слот.
таким образом, в среднем стирание займет элемент примерно на полпути через вектор, поэтому вам придется сдвинуть около половины элементов. Отсюда O(n)
. В лучшем случае, вы стираете последний элемент - нет необходимости скользить. В худшем случае вы сотрете первый элемент - должны затем перейти другой элемент.