Bir öğeyi belirli bir değere sahip bir stl vektöründen nasıl kaldırabilirim?


145

Ben stl vektör için API belgelerine bakıyordu ve vektör sınıfı üzerinde belirli bir değere sahip bir öğenin kaldırılmasına izin veren bir yöntem olduğunu fark ettim. Bu yaygın bir işlem gibi görünüyor ve bunu yapmak için yerleşik bir yöntem olmadığı garip görünüyor.


2
Bunu daha önce birkaç kez bahsettiğimi biliyorum ama Scott Meyer'ın Effective STL kitabı bu gotcha'ları açık bir şekilde ele alıyor.
Rob Wells


Bu sizin için ilginç bir okuma olabilir: en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

Yanıtlar:


165

std::removeöğeyi aslında kaptan silmez, ancak container_type::eraseşu anda kabın sonunda bulunan ekstra öğelerin GERÇEK sökülmesini yapmak için geçirilebilen yeni uç yineleyiciyi döndürür :

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
Bu hepsi bir arada ifade formu, derleyicinin bağımsız değişkenleri değerlendirme sırasına mı bağlı veya vec.end()çağrının her iki tarafında da aynı olacağı garanti ediliyor std::removemu? Bu güvenli gibi web'in diğer bölümlerini okurken bana benziyor, ancak açıkça belirtilmelidir.
dmckee --- eski moderatör kedi yavrusu

1
Bu iyi. Sonucunun vec.end()aynı olması gerekmez; sadece doğru olması gerekir (ki bu).
Jim Buck

8
vec.end()aynı olması gerekir, ancak sorun değil, çünkü std::removebunu değiştirmez. Eğer değiştirdiyse (ve eski değeri geçersiz kıldıysa) bir sorun olurdu: parametrelerin değerlendirme sırası belirtilmemiş ve bu nedenle ikincisinin vec.end()hala kullanıldığı zamana kadar geçerli olup olmadığını bilemezsiniz . Aynı olmasının nedeni basittir, std::removekabın boyutunu değiştirmez, sadece içeriği hareket ettirir.
Steve Jessop

2
Aynı sorunu yaşadığım için bu soruyu önemli buldum. Ancak, görsel stüdyom std::removesadece bir argümanla ele alıyor ; yani const char *_Filename. Hangi yöntemi çağırmam gerekiyor?
Victor

15
Bu removebir dosyayı silen sürümüdür . Kapsayıcılarla ilgilenen <algorithm>sürümüne erişmek için eklemeniz gerekir remove.
Jim Buck

64

Kaldırmak isterseniz bir öğeyi, şu biraz daha verimli olacaktır.

std::vector<int> v;


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

ya da sipariş sizin için önemli değilse öğeleri hareket ettirmekten kaçınabilirsiniz:

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
Std :: remove_if yaklaşımı mevcutsa, öğenin yinelenen öğelerini kaldırmayacağını unutmayın.
Den-Jason

15

Std :: remove genel yöntemini begin ve end iterator ile kullanın ve ardından öğeleri gerçekten kaldırmak için std :: vector.erase kullanın.

Dokümantasyon bağlantıları
std :: http://www.cppreference.com/cppalgorithm/remove.html
std :: vector.erase http://www.cppreference.com/cppvector/erase.html adresini kaldır

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

Benim hatamı işaret ettiği için Jim Buck'a teşekkürler.


Bu, vektörün ortasındaki bir elemanın silinmesi üzerine diğer elemanların hareketini önleme yeteneğine sahiptir, bu nedenle bu daha hızlıdır. Onları önce vektörün sonuna taşır, ardından vektörün sonundan atabilirsiniz.
Etherealone

5

Diğer cevaplar bunu nasıl yapacağımı kapsıyor, ancak bunun vektör API'sında olmasının gerçekten tuhaf olmadığını da düşündüm: değer için vektör boyunca verimsiz, doğrusal arama, ardından bir grup kopyalamak için.

Bu işlemi yoğun bir şekilde yapıyorsanız, bu nedenle bunun yerine std :: set'i düşünmeye değer olabilir.


5

Sıralanmamış bir vektörünüz varsa, o zaman son vektör elemanıyla değiştirebilirsiniz resize().

Sıralı bir kap ile, en iyi kapalı olacak std::vector::erase(). Unutmayın, içinde bir std::remove()tanım var <algorithm>, ama bu aslında silmiyor. (Belgeleri dikkatle okuyun).



3

Gönderen c ++ 20 :

std::eraseGirilen vektör ve değeri girdi olarak alan üye olmayan bir işlev tanıtıldı .

örn:

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

2
Sadece bu da değil, her bir konteyner için aşırı yüklü!
HolyBlackCat

... ve konteynere özgü özelliklerden faydalanır, örneğin map::erase!
LF

2

Bir yüklem kullanmak için ayrıca bkz. Std :: remove_if ...

Yukarıdaki bağlantıdan örnek:

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
Lütfen bu gönderiye daha fazla ayrıntı ekleyin. Haliyle, içeriğinin çoğunluğu bir bağlantıdan gelir ve bağlantı koparsa kaybolur.
Mick MacCallum

ne bind2nd gerçeği - - (C ++ 11'de kullanımdan kaldırıldı) (C ++ 17'de kaldırıldı)
Idan Banani

0

Ekstra bir şey olmadan yapmak istiyorsanız:

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

Özellikle bir öğeyi silmek için kullanabileceğiniz iki yol vardır. bir vektör alalım

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) Verimli olmayan yol: Oldukça etkili gibi görünse de, silme işlevi öğeleri sildiğinden ve tüm öğeleri 1 tarafından sola doğru kaydırdığı için değil, bu yüzden karmaşıklığı O (n ^ 2) olacaktır.

std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
   if(*itr == value)
   { 
      v.erase(itr);
   }
   else
       ++itr;
}

2) Etkili bir yol (TAVSİYE) : ERASE - REMOVE deyimleri olarak da bilinir .

  • std :: remove, verilen aralığı, kabın başlangıcına kaydırılan belirli bir öğeye eşit olmayan tüm öğelerle bir aralığa dönüştürür.
  • Yani, eşleşen öğeleri kaldırmayın. Sadece eşleşmemiş olanı başlangıca kaydırdı ve yeni geçerli sona bir yineleyici verdi. Sadece O (n) karmaşıklığı gerektirir.

kaldırma algoritmasının çıktısı:

10 20 30 50 40 50 

çünkü kaldırma türünün kaldırılması bu aralığın yeni sonuna yineleyicidir.

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

Şimdi öğeleri yeni uçtan vektörün eski ucuna silmek için vektörün silme işlevini kullanın. O (1) zamanı gerektirir.

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

yani bu yöntem O (n)


0

*

C ++ topluluğu isteğinizi duydu :)

*

C ++ 20 bunu yapmanın kolay bir yolunu sunuyor. Bu kadar basit olur:

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

Std :: erase ve std :: erase_if öğelerini kontrol etmelisiniz .

Değerin tüm öğelerini (burada '0') kaldıramaz, aynı zamanda O (n) zaman karmaşıklığında da yapar. Bu en iyi alabilirsiniz.

Derleyiciniz C ++ 20'yi desteklemiyorsa, erase-remove deyimini kullanmalısınız :

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.