Попробуйте использовать std :: deque, который обеспечивает вставку и удаление на обоих концах.
Дарио
40
Нет, не стоит использовать deque только потому, что вы можете удалить элемент, это очень плохой совет. Существует множество причин, по которым вы можете использовать deque или vector. Это правда, что удаление элемента из вектора может быть дорогостоящим - особенно если вектор большой, но нет оснований думать, что deque будет лучше, чем вектор из примера кода, который вы только что опубликовали.
Сова
6
Например, если у вас есть графическое приложение, в котором вы отображаете «список» вещей, в который вы вставляете / удаляете вещи в интерактивном режиме, подумайте, что вы просматриваете список 50-100 раз каждую секунду, чтобы отобразить их, и вы добавляете / удаляете вещи несколько раз. раз каждую минуту. Поэтому реализация «списка» как вектора, вероятно, является лучшим вариантом с точки зрения общей эффективности.
Мишель Бийо,
Ответы:
706
Чтобы удалить один элемент, вы можете сделать:
std::vector<int> vec;
vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);// Deletes the second element (vec[1])
vec.erase(vec.begin()+1);
Или удалить более одного элемента одновременно:
// Deletes the second through third elements (vec[1], vec[2])
vec.erase(vec.begin()+1, vec.begin()+3);
Отметим также , двоичная operator+это не обязательно определяется для итераторов на других типах контейнеров, как list<T>::iterator(вы не можете сделать list.begin() + 2на условиях std::list, вы должны использовать std::advanceдля этого)
bobobobo
Вы утверждаете, что «+1» - это первый элемент myVector [0] или фактическая позиция myVector [1]
K - Токсичность в SO растет.
2
Заранее вы должны сохранить итератор в переменной. Если вы используете std :: next, вы можете сделать это в одной строке: vec.erase (next (begin (vec), 123));
Дани
8
Спасибо всем, кто ответил. Что мы должны думать о дизайне класса, когда такая простая операция, как удаление элемента, требует, чтобы он пришел в StackOverflow?
Пьер
5
@Pierre, потому что числовой индекс определенного элемента не является основной моделью доступа, итератор . Все функции, которые смотрят на элементы контейнера, используют итераторы этого контейнера. Напримерstd::find_if
Caleth
212
Метод erase в std :: vector перегружен, поэтому его, вероятно, будет понятнее
Но эта проблема возникает независимо от того, сколько у вас элементов.
Zyx 2000
15
если есть только один элемент, индекс равен 0, и вы получите, vec.begin()что является действительным.
Энн Куинн
28
Я хотел бы, чтобы кто-то упомянул, что vec.erase(0)не работает, но vec.erase(vec.begin()+0)(или без +0) работает. В противном случае я не получаю соответствующий вызов функции, поэтому я пришел сюда
qrtLs
@qrtLs vec.erase(0)может на самом деле скомпилироваться, если 0получится интерпретировать как константу нулевого указателя ...
Макс, что делает эту функцию лучше, чем: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }я не говорю, что лучше, просто спрашиваю из личного интереса и возвращаю лучший результат, который мог получить этот вопрос.
13
@JoeyvG: Поскольку a vector<T>::iteratorявляется итератором с произвольным доступом, ваша версия в порядке и, возможно, немного понятнее. Но версия, которую выложил Макс, должна прекрасно работать, если вы измените контейнер на другой, который не поддерживает итераторы с произвольным доступом
Lily Ballard
2
Это лучший ответ, так как он применим и к другим форматам контейнеров. Вы также можете использовать std :: next ().
Бим
Намного лучший подход, так как он не зависит от внутренних элементов контейнера.
BartoszKP
std :: advance требуется только в том случае, если вы думаете, что это не будет вектор, то есть список. Но как вы указали здесь, оператор + не будет проще? Согласно этому stackoverflow.com/questions/1668088/… возможен выигрыш в производительности с оператором +
Нил МакГилл
14
eraseМетод будет использоваться двумя способами:
Стирание одного элемента:
vector.erase( vector.begin()+3);// Deleting the fourth element
Стирание диапазона элементов:
vector.erase( vector.begin()+3, vector.begin()+5);// Deleting from fourth element to sixth element
Это дублирующий ответ почти через 7 лет после принятого ответа. Пожалуйста, не делай этого.
AlastairG
10
На самом деле, eraseфункция работает для двух профилей:
Удаление одного элемента
iterator erase (iterator position);
Удаление ряда элементов
iterator erase (iterator first, iterator last);
Поскольку std :: vec.begin () отмечает начало контейнера, и если мы хотим удалить i-й элемент в нашем векторе, мы можем использовать:
vec.erase(vec.begin()+ index);
Если вы посмотрите внимательно, vec.begin () - это просто указатель на начальную позицию нашего вектора, и добавление к нему значения i увеличивает указатель на позицию i, поэтому вместо этого мы можем получить доступ к указателю на i-й элемент следующим образом:
-1 Последняя строка не компилируется (по крайней мере, в VS2017). Код предполагает, что vector :: iterator неявно конструируется из необработанного указателя, что не требуется стандартом.
CuriousGeorge
1
Это особенно верно для отладочных итераторов
Нишант Сингх
9
Если у вас есть неупорядоченный вектор, вы можете воспользоваться тем, что он неупорядочен, и использовать то, что я видел у Дэна Хиггинса в CPPCON.
Так как порядок списка не имеет значения, просто возьмите последний элемент в списке и скопируйте его поверх элемента, который вы хотите удалить, затем вытолкните и удалите последний элемент.
Я думаю, что это лучший ответ, если вектор неупорядочен. Он не основан на предположении, что на iterator + indexсамом деле вернет вам позицию итератора в этом индексе, что неверно для всех итерируемых контейнеров. Это также постоянная сложность, а не линейная благодаря использованию обратного указателя.
theferrit32
1
Это полностью необходимо добавить в стандартную библиотеку, как unordered_removeи unordered_remove_if… если только это не было, и я пропустил это, что происходит все чаще и чаще в наши дни :)
Уилл Кроуфорд,
Если бы предложили использовать перемещение-назначение или своп вместо копирования-назначения.
Карстен С.
std::removeпереупорядочивает контейнер так, чтобы все элементы, которые будут удалены, были в конце, нет необходимости делать это вручную, как это, если вы используете C ++ 17.
keith
@ как std::removeпомочь? cppreference утверждает, что даже в C ++ 17 для всех removeперегрузок требуется предикат, и ни один из них не принимает индекс.
Поль Дю Буа
4
Если вы работаете с большими векторами (размер> 100 000) и хотите удалить много элементов, я бы порекомендовал сделать что-то вроде этого:
int main(int argc,char** argv){
vector <int> vec;
vector <int> vec2;for(int i =0; i <20000000; i++){
vec.push_back(i);}for(int i =0; i < vec.size(); i++){if(vec.at(i)%3!=0)
vec2.push_back(i);}
vec = vec2;
cout << vec.size()<< endl;}
Код берет каждое число в vec, которое нельзя разделить на 3, и копирует его в vec2. После этого он копирует vec2 в vec. Это довольно быстро. Для обработки 20 000 000 элементов этот алгоритм занимает всего 0,8 с!
Я сделал то же самое с методом стирания, и это занимает много-много времени:
Вы удалите n-й элемент вектора, но при удалении второго элемента все другие элементы вектора будут смещены, а размер вектора будет равен -1. Это может быть проблемой, если вы перебираете вектор, поскольку vector size () уменьшается. Если у вас есть проблема, подобная этой, предложенная ссылка предложила использовать существующий алгоритм в стандартной библиотеке C ++. и "удалить" или "удалить_if".
Предыдущие ответы предполагают, что у вас всегда есть подписанный индекс. К сожалению, std::vectorиспользует size_typeдля индексации, иdifference_type арифметики итераторов, поэтому они не работают вместе, если у вас включена опция -Wconversion и друзья. Это еще один способ ответить на вопрос, имея возможность обрабатывать как подписанные, так и неподписанные:
Ответы:
Чтобы удалить один элемент, вы можете сделать:
Или удалить более одного элемента одновременно:
источник
operator+
это не обязательно определяется для итераторов на других типах контейнеров, какlist<T>::iterator
(вы не можете сделатьlist.begin() + 2
на условияхstd::list
, вы должны использоватьstd::advance
для этого)std::find_if
Метод erase в std :: vector перегружен, поэтому его, вероятно, будет понятнее
когда вы хотите стереть только один элемент.
источник
vec.begin()
что является действительным.vec.erase(0)
не работает, ноvec.erase(vec.begin()+0)
(или без +0) работает. В противном случае я не получаю соответствующий вызов функции, поэтому я пришел сюдаvec.erase(0)
может на самом деле скомпилироваться, если0
получится интерпретировать как константу нулевого указателя ...источник
template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }
я не говорю, что лучше, просто спрашиваю из личного интереса и возвращаю лучший результат, который мог получить этот вопрос.vector<T>::iterator
является итератором с произвольным доступом, ваша версия в порядке и, возможно, немного понятнее. Но версия, которую выложил Макс, должна прекрасно работать, если вы измените контейнер на другой, который не поддерживает итераторы с произвольным доступомerase
Метод будет использоваться двумя способами:Стирание одного элемента:
Стирание диапазона элементов:
источник
На самом деле,
erase
функция работает для двух профилей:Удаление одного элемента
Удаление ряда элементов
Поскольку std :: vec.begin () отмечает начало контейнера, и если мы хотим удалить i-й элемент в нашем векторе, мы можем использовать:
Если вы посмотрите внимательно, vec.begin () - это просто указатель на начальную позицию нашего вектора, и добавление к нему значения i увеличивает указатель на позицию i, поэтому вместо этого мы можем получить доступ к указателю на i-й элемент следующим образом:
Итак, мы можем написать:
источник
Если у вас есть неупорядоченный вектор, вы можете воспользоваться тем, что он неупорядочен, и использовать то, что я видел у Дэна Хиггинса в CPPCON.
Так как порядок списка не имеет значения, просто возьмите последний элемент в списке и скопируйте его поверх элемента, который вы хотите удалить, затем вытолкните и удалите последний элемент.
источник
iterator + index
самом деле вернет вам позицию итератора в этом индексе, что неверно для всех итерируемых контейнеров. Это также постоянная сложность, а не линейная благодаря использованию обратного указателя.unordered_remove
иunordered_remove_if
… если только это не было, и я пропустил это, что происходит все чаще и чаще в наши дни :)std::remove
переупорядочивает контейнер так, чтобы все элементы, которые будут удалены, были в конце, нет необходимости делать это вручную, как это, если вы используете C ++ 17.std::remove
помочь? cppreference утверждает, что даже в C ++ 17 для всехremove
перегрузок требуется предикат, и ни один из них не принимает индекс.Если вы работаете с большими векторами (размер> 100 000) и хотите удалить много элементов, я бы порекомендовал сделать что-то вроде этого:
Код берет каждое число в vec, которое нельзя разделить на 3, и копирует его в vec2. После этого он копирует vec2 в vec. Это довольно быстро. Для обработки 20 000 000 элементов этот алгоритм занимает всего 0,8 с!
Я сделал то же самое с методом стирания, и это занимает много-много времени:
источник
Чтобы удалить элемент, используйте следующий способ:
Для более широкого обзора вы можете посетить: http://www.cplusplus.com/reference/vector/vector/erase/
источник
Я предлагаю прочитать это, так как я считаю, что это то, что вы ищете. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
Если вы используете, например,
Вы удалите n-й элемент вектора, но при удалении второго элемента все другие элементы вектора будут смещены, а размер вектора будет равен -1. Это может быть проблемой, если вы перебираете вектор, поскольку vector size () уменьшается. Если у вас есть проблема, подобная этой, предложенная ссылка предложила использовать существующий алгоритм в стандартной библиотеке C ++. и "удалить" или "удалить_if".
Надеюсь что это помогло
источник
Предыдущие ответы предполагают, что у вас всегда есть подписанный индекс. К сожалению,
std::vector
используетsize_type
для индексации, иdifference_type
арифметики итераторов, поэтому они не работают вместе, если у вас включена опция -Wconversion и друзья. Это еще один способ ответить на вопрос, имея возможность обрабатывать как подписанные, так и неподписанные:Удалять:
Принять:
источник
Вот еще один способ сделать это, если вы хотите удалить элемент, найдя его с его значением в векторе, вам просто нужно сделать это на векторе.
это удалит ваше значение отсюда. Спасибо
источник
Как насчет этого?
источник
самый быстрый способ (для программирования конкурсов по сложности времени () = константа)
может стереть 100M элемент в 1 секунду;
и самый читаемый способ:
vec.erase(vec.begin() + pos);
источник
vector<int>::iterator
не обязательно совпадает сint *