Как удалить элемент из вектора stl с определенным значением?

Я просматривал документацию API для вектора stl и заметил, что в классе вектора нет метода, который позволял бы удалить элемент с определенным значением. Это похоже на обычную операцию, и кажется странным, что для этого нет встроенного способа.

Связанный: stackoverflow.com/questions/3385229/…

bobobobo 19.03.2013 05:01

Я знаю, что упоминал об этом несколько раз раньше, но книга Скотта Мейера Эффективный STL ясно освещает эти проблемы.

Rob Wells 02.09.2008 20:29

Это может быть для вас интересным чтением: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

sergiol 28.05.2015 14:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
146
3
210 613
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Используйте глобальный метод 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

Спасибо Джиму Баку за указание на мою ошибку.

Это позволяет предотвратить перемещение других элементов при стирании элемента в середине вектора, поэтому это происходит быстрее. Он сначала перемещает их в конец вектора, затем вы можете просто отбросить их с конца вектора.

Etherealone 04.03.2015 14:46

См. Также 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".

Пожалуйста, добавьте подробности в этот пост. В его нынешнем виде большая часть его контента поступает по ссылке и будет потеряна, если ссылка когда-либо сломается.

Mick MacCallum 26.02.2014 21:44

как насчет того факта, что bind2nd - (не рекомендуется в C++ 11) (удалено в C++ 17)

Idan Banani 20.12.2018 17:35
Ответ принят как подходящий

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? Мне кажется, что при чтении других частей сети это безопасно, но об этом следует четко заявить.

dmckee --- ex-moderator kitten 06.11.2011 01:28

Все нормально. Результат vec.end() не обязательно должен быть таким же; это просто должно быть правильным (а это так).

Jim Buck 06.11.2011 05:50

vec.end() должен быть таким же, но это нормально, потому что std::remove его не меняет. Если бы он действительно изменил его (и аннулировал старое значение), тогда возникла бы проблема: порядок оценки параметров не указан, и поэтому вы не узнаете, действителен ли второй vec.end() к тому времени, когда он используется. Причина того же проста: std::remove не меняет размер контейнера, он просто перемещает содержимое.

Steve Jessop 17.01.2013 01:00

Я считаю этот вопрос важным, поскольку у меня такая же проблема. Но моя визуальная студия использует std::remove только с одним аргументом; то есть const char *_Filename. Какой метод мне нужно вызвать?

Victor 24.08.2013 16:28

Это версия remove, которая удаляет файл. Вам необходимо включить <algorithm>, чтобы получить доступ к версии remove, которая работает с контейнерами.

Jim Buck 24.08.2013 21:00

Чтобы решение с std :: remove работало, необходимо иметь #include <algorithm>, иначе будет ошибка: ошибка: невозможно преобразовать 'std :: basic_string <char> :: iterator…' в 'const char * для аргумента '1'… 'в моем Ubuntu 16.04

Yu Shen 23.09.2017 01:40

Безусловно! Однако это выходило за рамки вопроса.

Jim Buck 23.09.2017 05:27

как удалить по определенному индексу (не по значению)?

user25 10.04.2018 14:57

vec.erase (index_to_erase)?

Jim Buck 11.04.2018 06:39

Если у вас есть несортированный вектор, вы можете просто поменять местами последний элемент вектора, а затем 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.

Den-Jason 12.04.2018 14:35

Более короткое решение (которое не заставляет вас повторять имя вектора 4 раза) - использовать Boost:

#include <boost/range/algorithm_ext/erase.hpp>

// ...

boost::remove_erase(vec, int_to_remove);

См. http://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/algorithms/new/remove_erase.html

Если вы хотите сделать это без каких-либо дополнительных действий, включите:

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) Эффективный способ (РЕКОМЕНДУЕТСЯ): он также известен как УДАЛИТЬ - УДАЛИТЬ идиомы.

  • std :: remove преобразует данный диапазон в диапазон, в котором все сравниваемые элементы не равны данному элементу, сдвинутому в начало контейнера.
  • Так что на самом деле не удаляйте совпадающие элементы. Он просто переместил несоответствующее начало и предоставил итератору новый действительный конец. Это просто требует сложности O (n).

вывод алгоритма удаления:

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);

Мало того, он перегружен для каждого конкретного контейнера!

HolyBlackCat 15.05.2019 23:37

... и пользуется преимуществами специфичных для контейнера свойств, таких как map::erase!

L. F. 19.04.2020 09:21

*

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());

Другие вопросы по теме