Menghapus elemen dari vektor

101

Saya ingin menghapus elemen dari vektor menggunakan metode hapus. Tetapi masalahnya di sini adalah bahwa elemen tersebut tidak dijamin hanya terjadi sekali dalam vektor. Ini mungkin ada beberapa kali dan saya perlu menghapus semuanya. Kode saya adalah seperti ini:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    std::vector<int>::iterator endIter = myNumbers_in.end();
    for(; iter != endIter; ++iter)
    {
        if(*iter == number_in)
        {
            myNumbers_in.erase(iter);
        }
    }
}

int main(int argc, char* argv[])
{
    std::vector<int> myNmbers;
    for(int i = 0; i < 2; ++i)
    {
        myNmbers.push_back(i);
        myNmbers.push_back(i);
    }

    erase(myNmbers, 1);

    return 0;
}

Kode ini jelas macet karena saya mengubah ujung vektor sambil mengulanginya. Apa cara terbaik untuk mencapai ini? Yaitu adakah cara untuk melakukan ini tanpa melakukan iterasi melalui vektor beberapa kali atau membuat satu salinan vektor lagi?

Naveen
sumber

Jawaban:

167

Gunakan idiom hapus / hapus :

std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());

Apa yang terjadi adalah removememadatkan elemen yang berbeda dari nilai yang akan dihapus ( number_in) di awal vectordan mengembalikan iterator ke elemen pertama setelah rentang itu. Kemudian erasehapus elemen-elemen ini (yang nilainya tidak ditentukan).

Motti
sumber
2
std::remove()menggeser elemen sedemikian rupa sehingga elemen yang akan dihapus ditimpa. Algoritme tidak mengubah ukuran penampung, dan jika nelemen dihapus maka tidak ditentukan apa nelemen terakhir .
wilhelmtell
20
idiom hapus-hapus dijelaskan dalam Butir 32 dalam buku "STL Efektif: 50 Cara Khusus untuk Meningkatkan Penggunaan Anda dari Perpustakaan Template Standar" oleh Scott Meyers.
Alessandro Jacopson
1
@Benjin tidak diperlukan pembaruan, itu akan memanggil destruktor objek jika ada.
Motti
54
'Idiom' STL seperti ini membuat saya menggunakan Python untuk proyek kecil.
Johannes Overmann
1
@LouisDionne yang mengacu pada satu iterator overload, saya menggunakan dua iterator overload
Motti
53

Memanggil hapus akan membatalkan iterator, Anda dapat menggunakan:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    while (iter != myNumbers_in.end())
    {
        if (*iter == number_in)
        {
            iter = myNumbers_in.erase(iter);
        }
        else
        {
           ++iter;
        }
    }

}

Atau Anda bisa menggunakan std :: remove_if bersama dengan functor dan std :: vector :: erase:

struct Eraser
{
    Eraser(int number_in) : number_in(number_in) {}
    int number_in;
    bool operator()(int i) const
    {
        return i == number_in;
    }
};

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());

Alih-alih menulis functor Anda sendiri dalam hal ini, Anda dapat menggunakan std :: remove :

std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());

Di C ++ 11 Anda bisa menggunakan lambda alih-alih functor:

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());

Di C ++ 17 std :: eksperimental :: erase dan std :: eksperimental :: erase_if juga tersedia, di C ++ 20 ini (akhirnya) diganti namanya menjadi std :: erase dan std :: erase_if :

std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda

atau:

std::vector<int> myNumbers;
std::erase(myNumbers, number_in);
dalle
sumber
2
Mengapa menggunakan functor Anda sendiri saat Anda dapat menggunakan equal_to? :-P sgi.com/tech/stl/equal_to.html
Chris Jester-Young
3
Omong-omong, menelepon erasedengan removeadalah cara kanonik untuk melakukan ini.
Konrad Rudolph
1
Saya pikir dia melakukan persis seperti itu. tetapi dia harus menggunakan remove_if jika menggunakan functor iirc sendiri. atau cukup gunakan hapus tanpa functor
Johannes Schaub - litb
3
+1 Kode yang dieja hanya membantu saya dalam kompetisi pemrograman, sementara "gunakan saja idiom hapus-hapus" tidak.
13
  1. Anda dapat melakukan iterasi menggunakan akses indeks,

  2. Untuk menghindari kompleksitas O (n ^ 2) Anda dapat menggunakan dua indeks, i - indeks pengujian saat ini, j - indeks untuk menyimpan item berikutnya dan pada akhir siklus ukuran baru dari vektor.

kode:

void erase(std::vector<int>& v, int num)
{
  size_t j = 0;
  for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] != num) v[j++] = v[i];
  }
  // trim vector to new size
  v.resize(j);
}

Dalam kasus seperti itu, Anda tidak memiliki iterator yang tidak valid, kompleksitasnya adalah O (n), dan kode sangat ringkas dan Anda tidak perlu menulis beberapa kelas helper, meskipun dalam beberapa kasus menggunakan kelas helper bisa mendapatkan keuntungan dalam kode yang lebih fleksibel.

Kode ini tidak menggunakan erasemetode, tetapi menyelesaikan tugas Anda.

Menggunakan stl murni Anda dapat melakukan ini dengan cara berikut (ini mirip dengan jawaban Motti):

#include <algorithm>

void erase(std::vector<int>& v, int num) {
    vector<int>::iterator it = remove(v.begin(), v.end(), num);
    v.erase(it, v.end());
}
sergtk
sumber
4

Bergantung pada mengapa Anda melakukan ini, menggunakan std :: set mungkin merupakan ide yang lebih baik daripada std :: vector.

Ini memungkinkan setiap elemen terjadi hanya sekali. Jika Anda menambahkannya beberapa kali, hanya akan ada satu contoh untuk dihapus. Ini akan membuat operasi penghapusan menjadi sepele. Operasi penghapusan juga akan memiliki kompleksitas waktu yang lebih rendah daripada pada vektor, namun, menambahkan elemen lebih lambat di set sehingga mungkin tidak banyak menguntungkan.

Ini tentu saja tidak akan berfungsi jika Anda tertarik pada berapa kali elemen telah ditambahkan ke vektor Anda atau urutan elemen ditambahkan.

Laserallan
sumber
-1

Untuk menghapus elemen pertama, Anda dapat menggunakan:

vector<int> mV{ 1, 2, 3, 4, 5 }; 
vector<int>::iterator it; 

it = mV.begin(); 
mV.erase(it); 
Amit Vujic
sumber
1
Bukan itu pertanyaannya. Jawaban Anda 11 tahun kemudian sepertinya tidak relevan, Amit!
Asteroid Dengan Sayap