Я просматривал документацию API для вектора stl и заметил, что в классе вектора нет метода, который позволял бы удалить элемент с определенным значением. Это похоже на обычную операцию, и кажется странным, что для этого нет встроенного способа.
Я знаю, что упоминал об этом несколько раз раньше, но книга Скотта Мейера Эффективный STL ясно освещает эти проблемы.
Это может быть для вас интересным чтением: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom





Используйте глобальный метод std :: remove с начальным и конечным итераторами, а затем используйте std :: vector.erase для фактического удаления элементов.
Ссылки на документацию
std :: remove http://www.cppreference.com/cppalgorithm/remove.html
std :: vector.erase http://www.cppreference.com/cppvector/erase.html
std::vector<int> v;
v.push_back(1);
v.push_back(2);
//Vector should contain the elements 1, 2
//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);
//Erase the "removed" elements.
v.erase(newEnd, v.end());
//Vector should now only contain 2
Спасибо Джиму Баку за указание на мою ошибку.
Это позволяет предотвратить перемещение других элементов при стирании элемента в середине вектора, поэтому это происходит быстрее. Он сначала перемещает их в конец вектора, затем вы можете просто отбросить их с конца вектора.
См. Также std :: remove_if, чтобы использовать предикат ...
Вот пример по ссылке выше:
vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(2);
V.push_back(8);
V.push_back(5);
V.push_back(7);
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
// The output is "1 4 2 8 5 7"
vector<int>::iterator new_end =
remove_if (V.begin(), V.end(),
compose1(bind2nd(equal_to<int>(), 0),
bind2nd(modulus<int>(), 2)));
V.erase(new_end, V.end()); [1]
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
// The output is "1 5 7".
Пожалуйста, добавьте подробности в этот пост. В его нынешнем виде большая часть его контента поступает по ссылке и будет потеряна, если ссылка когда-либо сломается.
как насчет того факта, что bind2nd - (не рекомендуется в C++ 11) (удалено в C++ 17)
std::remove на самом деле не стирает элемент из контейнера, но возвращает новый конечный итератор, который может быть передан в container_type::erase для РЕАЛЬНОГО удаления дополнительных элементов, которые теперь находятся в конце контейнера:
std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());
Зависит ли эта форма «все в одном» от порядка, в котором компилятор оценивает аргументы, или vec.end() гарантированно будет одинаковым с обеих сторон вызова std::remove? Мне кажется, что при чтении других частей сети это безопасно, но об этом следует четко заявить.
Все нормально. Результат vec.end() не обязательно должен быть таким же; это просто должно быть правильным (а это так).
vec.end() должен быть таким же, но это нормально, потому что std::remove его не меняет. Если бы он действительно изменил его (и аннулировал старое значение), тогда возникла бы проблема: порядок оценки параметров не указан, и поэтому вы не узнаете, действителен ли второй vec.end() к тому времени, когда он используется. Причина того же проста: std::remove не меняет размер контейнера, он просто перемещает содержимое.
Я считаю этот вопрос важным, поскольку у меня такая же проблема. Но моя визуальная студия использует std::remove только с одним аргументом; то есть const char *_Filename. Какой метод мне нужно вызвать?
Это версия remove, которая удаляет файл. Вам необходимо включить <algorithm>, чтобы получить доступ к версии remove, которая работает с контейнерами.
Чтобы решение с std :: remove работало, необходимо иметь #include <algorithm>, иначе будет ошибка: ошибка: невозможно преобразовать 'std :: basic_string <char> :: iterator…' в 'const char * для аргумента '1'… 'в моем Ubuntu 16.04
Безусловно! Однако это выходило за рамки вопроса.
как удалить по определенному индексу (не по значению)?
vec.erase (index_to_erase)?
Если у вас есть несортированный вектор, вы можете просто поменять местами последний элемент вектора, а затем resize().
С заказанным контейнером вам будет лучше всего с std::vector::erase(). Обратите внимание, что в std::remove() определен <algorithm>, но на самом деле он не стирает. (Внимательно прочтите документацию).
В других ответах рассказывается, как это сделать хорошо, но я подумал, что также хочу указать, что не очень странно, что этого нет в векторном API: это неэффективный линейный поиск по вектору для значения, за которым следует куча копирования, чтобы удалить его.
Если вы выполняете эту операцию интенсивно, по этой причине может быть лучше рассмотреть std :: set.
Если вы хотите удалить элемент ан, следующее будет немного эффективнее.
std::vector<int> v;
auto it = std::find(v.begin(), v.end(), 5);
if (it != v.end())
v.erase(it);
или вы можете избежать накладных расходов на перемещение предметов, если порядок для вас не имеет значения:
std::vector<int> v;
auto it = std::find(v.begin(), v.end(), 5);
if (it != v.end()) {
using std::swap;
// swap the one to be removed with the last element
// and remove the item at the end of the container
// to prevent moving all items after '5' by one
swap(*it, v.back());
v.pop_back();
}
Обратите внимание, что это не приведет к удалению дубликатов элемента, если они существуют, в отличие от подхода std :: remove_if.
Более короткое решение (которое не заставляет вас повторять имя вектора 4 раза) - использовать Boost:
#include <boost/range/algorithm_ext/erase.hpp>
// ...
boost::remove_erase(vec, int_to_remove);
Если вы хотите сделать это без каких-либо дополнительных действий, включите:
vector<IComponent*> myComponents; //assume it has items in it already.
void RemoveComponent(IComponent* componentToRemove)
{
IComponent* juggler;
if (componentToRemove != NULL)
{
for (int currComponentIndex = 0; currComponentIndex < myComponents.size(); currComponentIndex++)
{
if (componentToRemove == myComponents[currComponentIndex])
{
//Since we don't care about order, swap with the last element, then delete it.
juggler = myComponents[currComponentIndex];
myComponents[currComponentIndex] = myComponents[myComponents.size() - 1];
myComponents[myComponents.size() - 1] = juggler;
//Remove it from memory and let the vector know too.
myComponents.pop_back();
delete juggler;
}
}
}
}
Есть два способа, которыми вы можете использовать, в частности, стереть элемент. возьмем вектор
std :: vector < int > v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(40);
v.push_back(50);
1) Неэффективный способ: Хотя это кажется довольно эффективным, но это не потому, что функция стирания удаляет элементы и сдвигает все элементы влево на 1. поэтому его сложность будет O (n ^ 2)
std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
if (*itr == value)
{
v.erase(itr);
}
else
++itr;
}
2) Эффективный способ (РЕКОМЕНДУЕТСЯ): он также известен как УДАЛИТЬ - УДАЛИТЬ идиомы.
вывод алгоритма удаления:
10 20 30 50 40 50
поскольку возвращаемый тип удаления - это итератор до нового конца этого диапазона.
template <class ForwardIterator, class T>
ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);
Теперь используйте функцию стирания вектора, чтобы удалить элементы от нового конца до старого конца вектора. Это требует времени O (1).
v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );
поэтому этот метод работает за O (n)
От C++ 20:
Функция, не являющаяся членом, представила std::erase, который принимает вектор и значение, которое нужно удалить, в качестве входных данных.
бывший:
std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);
Мало того, он перегружен для каждого конкретного контейнера!
... и пользуется преимуществами специфичных для контейнера свойств, таких как map::erase!
*
C++ community has heard your request :)
*
C++ 20 предоставляет простой способ сделать это сейчас. Это так просто, как:
#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);
Вы должны проверить std :: erase и std :: erase_if.
Он не только удалит все элементы значения (здесь «0»), но и сделает это с временной сложностью На). Это лучшее, что вы можете получить.
Если ваш компилятор не поддерживает C++ 20, вам следует использовать стереть-удалить идиомы:
#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Связанный: stackoverflow.com/questions/3385229/…