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.
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.
Odpowiedzi:
std::remove
w rzeczywistości nie usuwa elementu z kontenera, ale zwraca nowy iterator końca, który można przekazać, container_type::erase
aby 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());
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ć.
vec.end()
nie musi być taki sam; po prostu musi być poprawne (a tak jest).
vec.end()
musi być taki sam, ale to jest w porządku, ponieważ std::remove
tego 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::remove
nie zmienia rozmiaru pojemnika, po prostu przesuwa zawartość.
std::remove
tylko jeden argument; to znaczy const char *_Filename
. Jaką metodę mam zadzwonić?
remove
która usuwa plik. Musisz dołączyć <algorithm>
, aby uzyskać dostęp do wersji, remove
która dotyczy kontenerów.
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();
}
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.
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.
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ę).
Krótszym rozwiązaniem (które nie zmusza cię do 4-krotnego powtarzania nazwy wektora) byłoby użycie Boost:
#include <boost/range/algorithm_ext/erase.hpp>
// ...
boost::remove_erase(vec, int_to_remove);
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);
map::erase
!
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".
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;
}
}
}
}
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 .
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)
*
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());