Jak usunąć element z wektora STL o określonej wartości?


145

Przeglądałem dokumentację API dla wektora stl i zauważyłem, że w klasie vector nie ma metody, która pozwalałaby na usunięcie elementu o określonej wartości. Wydaje się to typową operacją i wydaje się dziwne, że nie ma na to wbudowanej metody.


2
Wiem, że wspominałem o tym już kilka razy, ale książka Scotta Meyera Efektywny STL omawia te problemy w jasny sposób.
Rob Wells


To może być dla Ciebie interesująca lektura: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Odpowiedzi:


165

std::removew rzeczywistości nie usuwa elementu z kontenera, ale zwraca nowy iterator końca, który można przekazać, container_type::eraseaby wykonać PRAWDZIWE usunięcie dodatkowych elementów, które są teraz na końcu kontenera:

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

3
Czy ta forma instrukcji typu „wszystko w jednym” zależy od kolejności, w jakiej kompilator ocenia argumenty, czy też vec.end()gwarantuje, że będzie taka sama po obu stronach wywołania std::remove? Wydaje mi się, że czytanie innych części sieci w ten sposób jest bezpieczne, ale należy to wyraźnie stwierdzić.
dmckee --- ex-moderator kitten

1
W porządku. Wynik vec.end()nie musi być taki sam; po prostu musi być poprawne (a tak jest).
Jim Buck

8
vec.end()musi być taki sam, ale to jest w porządku, ponieważ std::removetego nie zmienia. Gdyby to zmienił (i unieważnił starą wartość), pojawiłby się problem: kolejność oceny parametrów jest nieokreślona i dlatego nie wiedziałbyś, czy druga vec.end()jest nadal ważna do czasu jej użycia. Powód jest taki sam, jest prosty, std::removenie zmienia rozmiaru pojemnika, po prostu przesuwa zawartość.
Steve Jessop,

2
Uważam, że to pytanie jest ważne, ponieważ mam ten sam problem. Ale moje studio wizualne ma std::removetylko jeden argument; to znaczy const char *_Filename. Jaką metodę mam zadzwonić?
Victor,

15
To jest wersja, removektóra usuwa plik. Musisz dołączyć <algorithm>, aby uzyskać dostęp do wersji, removektóra dotyczy kontenerów.
Jim Buck

64

Jeśli chcesz usunąć to element, dodaje się będzie nieco bardziej wydajny.

std::vector<int> v;


auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
    v.erase(it);

lub możesz uniknąć kosztów związanych z przenoszeniem przedmiotów, jeśli kolejność nie ma dla Ciebie znaczenia:

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

3
Zauważ, że nie usunie to duplikatów elementu, jeśli istnieją, podczas gdy podejście std :: remove_if tak.
Den-Jason

15

Użyj globalnej metody std :: remove z iteratorem begin i end, a następnie użyj std :: vector.erase, aby faktycznie usunąć elementy.

Linki do dokumentacji
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

Dziękuję Jimowi Buckowi za wskazanie mojego błędu.


Ma to zdolność do zapobiegania przemieszczaniu się innych elementów podczas wymazywania elementu w środku wektora, dlatego jest to szybsze. Najpierw przesuwa je na koniec wektora, a następnie możesz po prostu odrzucić z końca wektora.
Etherealone

5

Inne odpowiedzi dotyczą tego, jak to zrobić dobrze, ale pomyślałem, że też nie jest dziwne, że nie ma tego w wektorowym API: jest to nieefektywne, liniowe przeszukiwanie wektora w poszukiwaniu wartości, po którym następuje kilka kopiowania, aby go usunąć.

Jeśli wykonujesz tę operację intensywnie, warto rozważyć zamiast tego std :: set.


5

Jeśli masz nieposortowany wektor, możesz po prostu zamienić go z ostatnim elementem wektora resize() .

Z uporządkowanym pojemnika, będziesz najlepszy mecz std::vector::erase(). Zauważ, że istnieje std::remove()zdefiniowane w <algorithm>, ale to faktycznie nie powoduje wymazywania. (Przeczytaj uważnie dokumentację).



3

Od c ++ 20 :

Wprowadzono funkcję niebędącą składową std::erase, która przyjmuje wektor i wartość do usunięcia jako dane wejściowe.

dawny:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);

2
Nie tylko to, jest przeciążony dla każdego konkretnego kontenera!
HolyBlackCat

... i korzysta z właściwości specyficznych dla kontenera, takich jak map::erase!
LF

2

Zobacz także std :: remove_if aby móc użyć predykatu ...

Oto przykład z linku powyżej:

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".

2
Dodaj więcej szczegółów do tego posta. W obecnej postaci większość treści pochodzi z linku i zostałaby utracona, gdyby kiedykolwiek łącze się zepsuło.
Mick MacCallum

A co z faktem, że bind2nd to - (przestarzałe w C ++ 11) (usunięte w C ++ 17)
Idan Banani

0

Jeśli chcesz to zrobić bez żadnych dodatków zawiera:

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

0

Istnieją dwa sposoby, za pomocą których można w szczególności usunąć element. weźmy wektor

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) Sposób nieefektywny: Chociaż wydaje się być dość wydajny, ale nie dzieje się tak dlatego, że funkcja erase usuwa elementy i przesuwa wszystkie elementy w lewo o 1., więc jej złożoność będzie wynosić 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) Wydajny sposób (ZALECANE) : Znany również jako ERASE - REMOVE idioms .

  • std :: remove przekształca podany zakres w zakres ze wszystkimi porównywalnymi elementami, które nie są równe podanemu elementowi, przesuniętymi na początek kontenera.
  • Więc właściwie nie usuwaj dopasowanych elementów. Po prostu przesunął niedopasowane do początkowego i daje iterator do nowego prawidłowego końca. Wymaga tylko złożoności O (n).

wyjście algorytmu usuwania to:

10 20 30 50 40 50 

jako zwracany typ usunięcia jest iteratorem do nowego końca tego zakresu.

template <class ForwardIterator, class T>
  ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

Teraz użyj funkcji erase vector, aby usunąć elementy od nowego końca do starego końca wektora. Wymaga O (1) czasu.

v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );

więc ta metoda działa w O (n)


0

*

Społeczność C ++ usłyszała Twoją prośbę :)

*

C ++ 20 zapewnia teraz łatwy sposób na zrobienie tego. To staje się tak proste, jak:

#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);

Powinieneś sprawdzić std :: erase i std :: erase_if .

Nie tylko usunie wszystkie elementy wartości (tutaj '0'), zrobi to w O (n) złożoności czasowej . To najlepsze, co możesz dostać.

Jeśli Twój kompilator nie obsługuje C ++ 20, powinieneś użyć idiomu erase-remove :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.