Bagaimana cara menghapus elemen dari std :: vector <> dengan indeks?


508

Saya memiliki std :: vector <int>, dan saya ingin menghapus elemen ke-n. Bagaimana aku melakukan itu?

std::vector<int> vec;

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

vec.erase(???);

4
Pertimbangkan untuk menggunakan std :: deque yang menyediakan penyisipan dan penghapusan di kedua ujungnya.
Dario

40
Tidak, jangan pertimbangkan menggunakan deque hanya karena Anda mungkin ingin menghapus suatu elemen, itu saran yang sangat buruk. Ada banyak alasan mengapa Anda mungkin ingin menggunakan deque atau vektor. Memang benar bahwa menghapus elemen dari vektor bisa mahal - apalagi jika vektornya besar, tetapi tidak ada alasan untuk berpikir bahwa deque akan lebih baik daripada vektor dari contoh kode yang baru saja Anda posting.
Burung hantu

6
Misalnya, jika Anda memiliki aplikasi grafis tempat Anda menampilkan "daftar" hal-hal yang Anda sisipkan / hapus secara interaktif, pertimbangkan Anda menjalankan daftar 50-100 kali setiap detik untuk menampilkannya, dan Anda menambah / menghapus beberapa hal. kali setiap menit. Jadi menerapkan "daftar" sebagai vektor mungkin merupakan pilihan yang lebih baik dalam hal efisiensi total.
Michel Billaud

Jawaban:


705

Untuk menghapus satu elemen, Anda dapat melakukan:

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

Atau, untuk menghapus lebih dari satu elemen sekaligus:

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

50
Perhatikan juga biner operator+yang tidak tentu didefinisikan untuk iterator pada jenis wadah lainnya, seperti list<T>::iterator(Anda tidak dapat melakukan list.begin() + 2pada std::list, Anda harus menggunakan std::advanceuntuk itu)
bobobobo

apakah Anda menyatakan bahwa "+1" adalah elemen pertama myVector [0] atau posisi aktual myVector [1]
K - Toksisitas dalam SO bertambah.

2
Dengan muka Anda harus menyimpan iterator dalam variabel. Jika Anda menggunakan std :: next Anda dapat melakukannya dalam satu baris: vec.erase (next (begin (vec), 123));
dani

8
Terima kasih untuk semua yang telah menjawab. Apa yang kita pikirkan tentang desain kelas ketika operasi sederhana seperti menghapus elemen, mengharuskan seseorang untuk datang ke StackOverflow?
Pierre

5
@ Perier karena indeks numerik elemen tertentu bukan model utama akses, iterator . Semua fungsi yang melihat elemen dari wadah menggunakan iterator yang mengandung itu. Misalnyastd::find_if
Caleth

212

Metode hapus pada std :: vector kelebihan beban, jadi mungkin lebih jelas untuk memanggil

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

ketika Anda hanya ingin menghapus satu elemen.


3
Tetapi masalah itu muncul tidak peduli berapa banyak elemen yang Anda miliki.
Zyx 2000

15
jika hanya ada satu elemen, indeks adalah 0, dan Anda mendapatkan vec.begin()yang valid.
Anne Quinn

28
Saya berharap seseorang akan menyebutkan bahwa vec.erase(0)tidak berfungsi, tetapi vec.erase(vec.begin()+0)(atau tanpa +0) tidak. Kalau tidak, saya tidak mendapatkan panggilan fungsi yang cocok, itulah sebabnya saya datang ke sini
qrtLs

@ qrtLs vec.erase(0)sebenarnya dapat dikompilasi jika 0kebetulan ditafsirkan sebagai konstanta penunjuk nol ...
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, apa yang membuat fungsi itu lebih baik daripada: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }Saya tidak mengatakan keduanya lebih baik, hanya meminta karena minat pribadi dan mengembalikan hasil terbaik yang bisa didapat pertanyaan ini.

13
@ JoeyvG: Karena a vector<T>::iteratoradalah iterator akses acak, versi Anda baik-baik saja dan mungkin sedikit lebih jelas. Tetapi versi yang diposting Max akan bekerja dengan baik jika Anda mengganti wadah ke wadah lain yang tidak mendukung iterator akses acak
Lily Ballard

2
Ini juga jawaban yang lebih baik, karena ini berlaku untuk format wadah lainnya juga. Anda juga bisa menggunakan std :: next ().
Bim

Pendekatan yang jauh lebih baik karena tidak bergantung pada bagian dalam wadah.
BartoszKP

std :: advance hanya diperlukan jika Anda berpikir ini tidak akan menjadi vektor yaitu daftar. Tetapi seperti yang Anda tentukan di sini, apakah operator + tidak akan lebih sederhana? Menurut stackoverflow.com/questions/1668088/… ini ada kemungkinan peningkatan kinerja dengan operator +
Neil McGill

14

The eraseMetode yang akan digunakan dalam dua cara:

  1. Menghapus elemen tunggal:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Menghapus berbagai elemen:

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

7
Ini adalah jawaban rangkap hampir 7 tahun setelah jawaban yang diterima. Tolong jangan lakukan ini.
AlastairG

10

Sebenarnya, erasefungsi ini berfungsi untuk dua profil:

  • Menghapus satu elemen

    iterator erase (iterator position);
  • Menghapus serangkaian elemen

    iterator erase (iterator first, iterator last);

Karena std :: vec.begin () menandai awal dari wadah dan jika kita ingin menghapus elemen ke-i dalam vektor kita, kita dapat menggunakan:

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

Jika Anda melihat lebih dekat, vec.begin () hanyalah sebuah pointer ke posisi awal vektor kita dan menambahkan nilai i ke dalamnya akan menambah pointer ke posisi i, jadi alih-alih kita dapat mengakses pointer ke elemen engan dengan:

&vec[i]

Jadi kita bisa menulis:

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

6
-1 Baris terakhir tidak dikompilasi (setidaknya dalam VS2017). Kode mengasumsikan bahwa vektor :: iterator secara implisit dapat dibangun dari pointer mentah, yang tidak diperlukan oleh standar.
CuriousGeorge

1
Ini benar terutama untuk debug iterators
Nishant Singh

9

Jika Anda memiliki vektor tidak berurutan, Anda dapat memanfaatkan fakta bahwa itu tidak berurutan dan menggunakan sesuatu yang saya lihat dari Dan Higgins di CPPCON

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

Karena urutan daftar tidak masalah, ambil saja elemen terakhir dalam daftar dan salin ke atas item yang ingin Anda hapus, lalu pop dan hapus item terakhir.


Saya pikir ini adalah jawaban terbaik jika vektornya tidak berurutan. Itu tidak bergantung pada asumsi yang iterator + indexbenar-benar akan memberi Anda kembali posisi iterator pada indeks itu, yang tidak berlaku untuk semua wadah yang dapat diubah. Ini juga merupakan kompleksitas konstan daripada linier dengan mengambil keuntungan dari pointer belakang.
theferrit32

1
Ini benar-benar perlu ditambahkan ke std lib sebagai unordered_removedan unordered_remove_if... kecuali jika sudah dan saya melewatkannya, yang terjadi lebih dan lebih sering akhir-akhir ini :)
Will Crawford

Jika akan menyarankan untuk menggunakan penugasan pindah atau menukar bukan penugasan salinan.
Carsten S

std::removeatur ulang wadah sehingga semua elemen yang akan dihapus ada di akhir, tidak perlu melakukannya secara manual seperti ini jika Anda menggunakan C ++ 17.
keith

@ dengan bagaimana std::removemembantu? cppreference mengklaim bahwa bahkan di C ++ 17, semua removeoverload memerlukan predikat, dan tidak ada yang mengambil indeks.
Paul Du Bois

4

Jika Anda bekerja dengan vektor besar (ukuran> 100.000) dan ingin menghapus banyak elemen, saya akan merekomendasikan untuk melakukan sesuatu seperti ini:

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

Kode mengambil setiap angka dalam vec yang tidak dapat dibagi dengan 3 dan menyalinnya ke vec2. Setelah itu menyalin vec2 di vec. Cukup cepat. Untuk memproses 20.000.000 elemen, algoritma ini hanya membutuhkan 0,8 detik!

Saya melakukan hal yang sama dengan metode erase, dan itu membutuhkan banyak waktu:

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
bagaimana ini menjawab pertanyaan?
Regis Portalez

4
Menarik, tetapi tidak relevan dengan pertanyaan!
Roddy

Bukankah algoritma di tempat lebih cepat?
user202729

2
itu std :: remove_if (+ erase)
RiaD


3

Saya sarankan untuk membaca ini karena saya percaya itulah yang Anda cari. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Jika Anda menggunakan misalnya

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

Anda akan menghapus elemen ke-n vektor tetapi ketika Anda menghapus elemen kedua, semua elemen vektor lainnya akan digeser dan ukuran vektor akan menjadi -1. Ini bisa menjadi masalah jika Anda mengulang vektor karena ukuran vektor () menurun. Jika Anda memiliki masalah seperti tautan yang disediakan ini, disarankan untuk menggunakan algoritma yang ada di pustaka C ++ standar. dan "hapus" atau "hapus_kan".

Semoga ini bisa membantu


0

Jawaban sebelumnya mengasumsikan bahwa Anda selalu memiliki indeks yang ditandatangani. Sayangnya, std::vectorkegunaan size_typeuntuk pengindeksan, dan difference_typeuntuk iterator aritmatika, sehingga mereka tidak bekerja bersama jika Anda memiliki "-Konversi" dan teman diaktifkan. Ini adalah cara lain untuk menjawab pertanyaan, sambil dapat menangani keduanya yang ditandatangani dan tidak ditandatangani:

Untuk menghapus:

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

Untuk mengambil:

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

di sini adalah satu lagi cara untuk melakukan ini jika Anda ingin menghapus elemen dengan menemukan ini dengan nilainya dalam vektor, Anda hanya perlu melakukan ini pada vektor.

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

itu akan menghapus nilai Anda dari sini. Terima kasih


0

Bagaimana dengan ini?

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

cara tercepat (untuk kontes pemrograman dengan kompleksitas waktu () = konstan)

dapat menghapus item 100M dalam 1 detik;

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

dan cara yang paling mudah dibaca: vec.erase(vec.begin() + pos);


2
Ini sangat non-portabel; ini akan bekerja dengan libstdc ++, tetapi tidak libc ++, dan tidak dengan MSVC. vector<int>::iteratorbelum tentu sama denganint *
Marshall Clow

2
Ini menjijikkan, saya pikir saya akan mengubah libstdc ++ untuk menghentikannya dari bekerja.
Jonathan Wakely
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.