Bir öğeyi std :: vector <> dizininden nasıl silebilirim?


508

Bir std :: vector <int> var ve n'th öğeyi silmek istiyorum. Bunu nasıl yaparım?

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

vec.erase(???);

4
Her iki uçta ekleme ve silme sağlayan bir std :: deque kullanmayı düşünün.
Dario

40
Hayır, sadece bir öğeyi silmek isteyebileceğiniz için deque kullanmayı düşünmeyin, bu gerçekten kötü bir tavsiye. Deque veya vector kullanmak isteyebileceğiniz birçok neden var. Bir vektörden bir öğeyi silmenin maliyetli olabileceği doğrudur - eğer vektör büyükse, ama bir deque'nin az önce yayınladığınız kod örneğindeki bir vektörden daha iyi olacağını düşünmek için bir neden yoktur.
Baykuş

6
Örneğin, bir şeyi etkileşimli olarak eklediğiniz / çıkardığınız şeylerin bir "listesini" görüntülediğiniz bir grafik uygulamanız varsa, bunları görüntülemek için her saniye 50-100 kez listeden geçtiğinizi ve birkaç şey eklediğinizde / kaldırdığınızı düşünün dakikada bir kez. Dolayısıyla "liste" nin bir vektör olarak uygulanması, toplam verimlilik açısından muhtemelen daha iyi bir seçenektir.
Michel Billaud

Yanıtlar:


705

Tek bir öğeyi silmek için şunları yapabilirsiniz:

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

// Deletes the second element (vec[1])
vec.erase(vec.begin() + 1);

Veya aynı anda birden fazla öğeyi silmek için:

// Deletes the second through third elements (vec[1], vec[2])
vec.erase(vec.begin() + 1, vec.begin() + 3);

50
Ayrıca ikili Not operator+edilir değil ille gibi diğer konteyner türlerinde yineleyiciler için tanımlanan list<T>::iterator(yapamayacağınız list.begin() + 2bir de std::list, kullanmak zorunda std::advanceolduğu için)
bobobobo

"+1" nin ilk eleman myVector [0] veya gerçek konum myVector [1]
K -

2
Önceden yineleyiciyi bir değişkene kaydetmelisiniz. Std :: next kullanıyorsanız bunu bir satırda yapabilirsiniz: vec.erase (next (begin (vec), 123));
dani

8
Yanıt veren herkese teşekkürler. Bir öğeyi silmek gibi basit bir işlem, birinin StackOverflow'a gelmesini gerektirdiğinde sınıf tasarımını ne düşünürüz?
Pierre

5
@Pierre, belirli bir öğenin sayısal dizini birincil erişim modeli olmadığından yineleyicidir . Bir kabın öğelerine bakan tüm işlevler, kabın yineleyicilerini kullanır. Ör.std::find_if
Caleth

212

Std :: vector'daki silme yöntemi aşırı yüklenmiş, bu nedenle aramak daha açıktır

vec.erase(vec.begin() + index);

yalnızca tek bir öğeyi silmek istediğinizde.


3
Ancak bu sorun kaç elementiniz olursa olsun ortaya çıkar.
Zyx 2000

15
yalnızca bir öğe varsa, dizin 0'dır ve böylece vec.begin()geçerli olanı alırsınız .
Anne Quinn

28
Keşke birisi vec.erase(0)işe yaramazsa bahsetmişti , ama vec.erase(vec.begin()+0)(ya da +0 olmadan). Aksi takdirde, eşleşen bir işlev çağrısı
almıyorum

@qrtLs , boş gösterici sabiti olarak yorumlanırsa vec.erase(0)gerçekten derlenebilir 0...
LF

57
template <typename T>
void remove(std::vector<T>& vec, size_t pos)
{
    std::vector<T>::iterator it = vec.begin();
    std::advance(it, pos);
    vec.erase(it);
}

2
Max, bu işlevi bundan daha iyi yapan şey: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }Ben sadece daha iyi olduğunu söylemiyorum, sadece kişisel çıkarlarını sormak ve bu sorunun elde edebileceği en iyi sonucu döndürmek.

13
@JoeyvG: vector<T>::iteratora rastgele erişimli bir yineleyici olduğundan, sürümünüz gayet iyi ve belki biraz daha net. Ancak, kapsayıcıyı rasgele erişimli yineleyicileri desteklemeyen bir başkasına değiştirirseniz, Max'in yayınladığı sürüm gayet iyi çalışmalıdır
Lily Ballard

2
Bu, diğer kapsayıcı formatları için de geçerli olduğu için daha iyi bir cevaptır. Std :: next () öğesini de kullanabilirsiniz.
Bim

Konteynerin iç kısımlarına bağlı olmadığı için çok daha iyi bir yaklaşım.
BartoszKP

std :: advance sadece bunun bir vektör yani bir liste olmayacağını düşünüyorsanız gereklidir. Ama burada belirttiğiniz gibi, operatör + daha basit olmaz mıydı? Bu stackoverflow.com/questions/1668088/… 'e göre operatör + ile performansta olası bir kazanç var
Neil McGill

14

eraseYöntem iki şekilde kullanılacaktır:

  1. Tek eleman silme:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Eleman aralığının silinmesi:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element

7
Bu, kabul edilen cevaptan yaklaşık 7 yıl sonra tekrarlanan bir cevaptır. Lütfen bunu yapma.
AlastairG

10

Aslında, erasefonksiyon iki profil için çalışır:

  • Tek bir öğeyi kaldırma

    iterator erase (iterator position);
  • Bir dizi öğeyi kaldırma

    iterator erase (iterator first, iterator last);

Std :: vec.begin (), kabın başlangıcını işaretlediğinden ve vektörümüzdeki ith öğesini silmek istiyorsak, şunları kullanabiliriz:

vec.erase(vec.begin() + index);

Eğer yakından bakarsanız, vec.begin () sadece vektörümüzün başlangıç ​​konumuna bir işaretçi olur ve i'nin değerini ona ekleyerek işaretçiyi i konumuna yükseltir, bunun yerine işaretçiyi ith öğesine öğeye erişebiliriz:

&vec[i]

Böylece şunu yazabiliriz:

vec.erase(&vec[i]); // To delete the ith element

6
-1 Son satır derlenmez (en azından VS2017'de). Kod, vector :: iterator öğesinin standart tarafından gerekli olmayan bir ham işaretçiden örtük olarak oluşturulabileceğini varsayar.
CuriousGeorge

1
Bu özellikle hata ayıklayıcı
yinelemeleri

9

Sıralanmamış bir vektörünüz varsa, sıralanmamış olmasından faydalanabilir ve Dan Higgins'den CPPCON'da gördüğüm bir şeyi kullanabilirsiniz

template< typename TContainer >
static bool EraseFromUnorderedByIndex( TContainer& inContainer, size_t inIndex )
{
    if ( inIndex < inContainer.size() )
    {
        if ( inIndex != inContainer.size() - 1 )
            inContainer[inIndex] = inContainer.back();
        inContainer.pop_back();
        return true;
    }
    return false;
}

Liste sırası önemli olmadığından, listedeki son öğeyi alın ve kaldırmak istediğiniz öğenin üstüne kopyalayın, ardından son öğeyi pop ve silin.


Vektör sıralanmamışsa, bunun en iyi cevap olduğunu düşünüyorum. Bu iterator + indexindekste yineleyici pozisyonunu size geri verecek varsayımlara dayanmaz , bu da tüm tekrarlanabilir kaplar için geçerli değildir. Ayrıca, arka işaretçiden faydalanarak doğrusal yerine sabit karmaşıklıktır.
theferrit32

1
Bu tamamen std lib unordered_removeve gibi eklenmelidir gerekir unordered_remove_if... olmadıkça ve ben özledim, bu günlerde daha sık oluyor :)
Will Crawford

Bir kopya ataması yerine taşıma ataması veya takas kullanılmasını önerirseniz.
Carsten S

std::removekabı, kaldırılacak tüm öğelerin sonunda olacak şekilde yeniden sıralar, C ++ 17 kullanıyorsanız manuel olarak böyle yapmanıza gerek yoktur.
keith

@keith nasıl std::removeyardımcı olur? cppreference, C ++ 17'de bile tüm removeaşırı yüklerin yüklem gerektirdiğini ve hiçbirinin bir indeks almadığını iddia ediyor .
Paul Du Bois

4

Büyük vektörlerle (boyut> 100.000) çalışıyorsanız ve çok sayıda öğeyi silmek istiyorsanız, böyle bir şey yapmanızı tavsiye ederim:

int main(int argc, char** argv) {

    vector <int> vec;
    vector <int> vec2;

    for (int i = 0; i < 20000000; i++){
        vec.push_back(i);}

    for (int i = 0; i < vec.size(); i++)
    {
        if(vec.at(i) %3 != 0)
            vec2.push_back(i);
    }

    vec = vec2;
    cout << vec.size() << endl;
}

Kod, vec cinsinden 3'e bölünemeyen her sayıyı alır ve vec2'ye kopyalar. Daha sonra vec2'yi vec olarak kopyalar. Oldukça hızlı. 20.000.000 elementi işlemek için bu algoritma sadece 0.8 saniye sürer!

Aynı şeyi erase yöntemiyle yaptım ve çok zaman alıyor:

Erase-Version (10k elements)  : 0.04 sec
Erase-Version (100k elements) : 0.6  sec
Erase-Version (1000k elements): 56   sec
Erase-Version (10000k elements): ...still calculating (>30 min)

6
bu soruya nasıl cevap veriyor?
Regis Portalez

4
İlginç, ama soru ile ilgili değil!
Roddy

Yerinde algoritma daha hızlı olmaz mı?
user202729

2
bu std :: remove_if (+ silme)
RiaD


3

Aradığın şey olduğuna inandığım için bunu okumayı öneririm. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Örneğin kullanıyorsanız

 vec.erase(vec.begin() + 1, vec.begin() + 3);

vektörün n. elemanını sileceksiniz, ancak ikinci elemanı sildiğinizde, vektörün diğer tüm elemanları kaydırılacak ve vektör boyutu -1 olacaktır. Vektör boyutu () azaldığı için vektörde döngü yaparsanız bu sorun olabilir. Bu tür bir sorun varsa, standart C ++ kütüphanesinde mevcut algoritmayı kullanmanız önerilir. ve "remove" veya "remove_if".

Umarım bu yardımcı olmuştur


0

Önceki yanıtlar her zaman imzalı bir dizininizin olduğunu varsayar . Ne yazık ki, indeksleme ve yineleyici aritmetiği için std::vectorkullanır , bu nedenle "-Wconversion" ve arkadaşlarınız etkinse birlikte çalışmazlar. Bu, hem imzalı hem de imzasız olarak işlerken soruyu cevaplamanın başka bir yoludur:size_typedifference_type

Ayırmak:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
void remove(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);
    v.erase(iter);
}

Almak:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
T take(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);

    auto val = *iter;
    v.erase(iter);

    return val;
}

0

bir öğeyi vektörde değeriyle bularak silmek istiyorsanız bunu yapmanın bir yolu daha var, bunu vektörde yapmanız yeterlidir.

vector<int> ar(n);
ar.erase(remove(ar.begin(), ar.end()), (place your value here from vector array));

değerini buradan kaldıracaktır. Teşekkürler


0

Buna ne dersin?

void squeeze(vector<int> &v)
{
    int j = 0;
    for (int i = 1; i < v.size(); i++)
        if (v[i] != v[j] && ++j != i)
            v[j] = v[i];
    v.resize(j + 1);
}

-4

en hızlı yol (yarışmaların zaman karmaşıklığına göre programlanması için () = sabit)

100M öğeyi 1 saniyede silebilir;

    vector<int> it = (vector<int>::iterator) &vec[pos];
    vec.erase(it);

ve en okunabilir yolu: vec.erase(vec.begin() + pos);


2
Bu çok taşınabilir değildir; libstdc ++ ile çalışır, ancak libc ++ ile çalışmaz, MSVC ile çalışmaz. vector<int>::iteratormutlaka aynı değildirint *
Marshall Clow

2
İğrenç, sanırım çalışmasını durdurmak için libstdc ++ değiştireceğim.
Jonathan Wakely
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.