ฉันจะลบองค์ประกอบจาก std :: vector <> โดย index ได้อย่างไร


508

ฉันมี std :: vector <int> และฉันต้องการลบองค์ประกอบ n'th ฉันจะทำอย่างไร

std::vector<int> vec;

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

vec.erase(???);

4
พิจารณาใช้ std :: deque ซึ่งให้การแทรกและการลบที่ปลายทั้งสอง
Dario

40
ไม่ไม่ควรใช้ deque เพียงเพราะคุณอาจต้องการลบองค์ประกอบนั่นเป็นคำแนะนำที่แย่มาก มีเหตุผลมากมายที่คุณอาจต้องการใช้ deque หรือ vector เป็นความจริงที่การลบองค์ประกอบจากเวกเตอร์อาจมีค่าใช้จ่ายสูงโดยเฉพาะถ้าเวกเตอร์มีขนาดใหญ่ แต่ไม่มีเหตุผลที่จะคิดว่า deque จะดีกว่าเวกเตอร์จากตัวอย่างรหัสที่คุณเพิ่งโพสต์
Owl

6
ตัวอย่างเช่นหากคุณมีแอปพลิเคชันแบบกราฟิกที่คุณแสดง "รายการ" ของสิ่งที่คุณแทรก / ลบสิ่งต่าง ๆ แบบโต้ตอบพิจารณาคุณเรียกใช้ผ่านรายการ 50-100 ครั้งต่อวินาทีเพื่อแสดงและคุณเพิ่ม / ลบบางสิ่ง ครั้งต่อนาที ดังนั้นการใช้ "รายการ" เป็นเวกเตอร์อาจเป็นตัวเลือกที่ดีกว่าในแง่ของประสิทธิภาพโดยรวม
Michel Billaud

คำตอบ:


705

หากต้องการลบองค์ประกอบเดียวคุณสามารถทำได้:

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

หรือหากต้องการลบมากกว่าหนึ่งองค์ประกอบในคราวเดียว:

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

50
ยังทราบไบนารีoperator+จะไม่ได้กำหนดจำเป็นสำหรับ iterators ประเภทภาชนะอื่น ๆ เช่นlist<T>::iterator(คุณไม่สามารถทำlist.begin() + 2บนstd::listคุณจะต้องใช้std::advanceสำหรับที่)
bobobobo

คุณกำลังระบุว่า "+1" เป็นองค์ประกอบแรก myVector [0] หรือตำแหน่งจริง myVector [1]
K - ความเป็นพิษใน SO กำลังเพิ่มขึ้น

2
ล่วงหน้าคุณจะต้องบันทึกตัววนซ้ำในตัวแปร ถ้าคุณใช้ std :: next คุณสามารถทำได้ในหนึ่งบรรทัด: vec.erase (ถัดไป (เริ่มต้น (vec), 123));
dani

8
ขอบคุณทุกคนที่ตอบ เราจะคิดอย่างไรกับการออกแบบคลาสเมื่อการดำเนินการอย่างง่าย ๆ เช่นการลบองค์ประกอบต้องใช้หนึ่งใน StackOverflow
ปิแอร์

5
@Pierre เนื่องจากดัชนีตัวเลขขององค์ประกอบเฉพาะนั้นไม่ใช่รูปแบบหลักของการเข้าถึงตัววนซ้ำคือ ฟังก์ชันทั้งหมดที่ดูองค์ประกอบของคอนเทนเนอร์ใช้ตัววนซ้ำของคอนเทนเนอร์นั้น เช่นstd::find_if
Caleth

212

เมธอดการลบบน std :: vector มากเกินไปดังนั้นจึงอาจมีความชัดเจนในการโทร

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

เมื่อคุณต้องการลบองค์ประกอบเดียว


3
แต่ปัญหานั้นจะปรากฏขึ้นไม่ว่าคุณจะมีองค์ประกอบกี่ชิ้น
Zyx 2000

15
หากมีเพียงองค์ประกอบเดียวดัชนีคือ 0 และคุณจะได้รับvec.begin()ซึ่งถูกต้อง
Anne Quinn

28
ฉันหวังว่าจะมีใครบางคนกล่าวถึงว่าvec.erase(0)ใช้งานไม่ได้ แต่vec.erase(vec.begin()+0)(หรือไม่มี +0) มิฉะนั้นฉันจะไม่เรียกฟังก์ชันที่ตรงกันซึ่งเป็นสาเหตุที่ฉันมาที่นี่
qrtLs

@qrtLs vec.erase(0)จริง ๆ แล้วอาจทำการรวบรวมหาก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
แม็กซ์สิ่งที่ทำให้ฟังก์ชั่นนั้นดีกว่า: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }ฉันไม่ได้บอกว่ามันดีกว่าเพียงแค่ถามจากความสนใจส่วนตัวและเพื่อให้ได้ผลลัพธ์ที่ดีที่สุดคำถามนี้จะได้รับ

13
@JoeyvG: เนื่องจาก a vector<T>::iteratorเป็นตัววนซ้ำการเข้าถึงแบบสุ่มรุ่นของคุณจึงใช้ได้และอาจชัดเจนขึ้นเล็กน้อย แต่เวอร์ชันที่ Max โพสต์ควรใช้งานได้ดีถ้าคุณเปลี่ยนคอนเทนเนอร์ไปเป็นอีกอันที่ไม่รองรับการเข้าถึงแบบวนซ้ำ
Lily Ballard

2
นี่เป็นคำตอบที่ดีกว่าเพราะนำไปใช้กับรูปแบบคอนเทนเนอร์อื่นด้วย คุณสามารถใช้ std :: next ()
Bim

วิธีการที่ดีกว่ามากเพราะมันไม่ได้ขึ้นอยู่กับ internals ของภาชนะ
BartoszKP

std :: Advance มีความจำเป็นเฉพาะถ้าคุณคิดว่านี่จะไม่ใช่เวกเตอร์นั่นคือรายการ แต่ตามที่คุณระบุไว้ที่นี่ผู้ประกอบการ + จะไม่ง่ายขึ้นหรือไม่ ตามstackoverflow.com/questions/1668088/นี้มีการเพิ่มประสิทธิภาพการทำงานที่เป็นไปได้ด้วยตัวดำเนินการ +
Neil McGill

14

eraseวิธีการจะนำมาใช้ในสองวิธี

  1. การลบองค์ประกอบเดียว:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. การลบช่วงขององค์ประกอบ:

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

7
นี่เป็นคำตอบที่ซ้ำกันเกือบ 7 ปีหลังจากคำตอบที่ยอมรับ โปรดอย่าทำเช่นนี้
AlastairG

10

ที่จริงแล้วeraseฟังก์ชั่นนี้ใช้งานได้สองโปรไฟล์:

  • การลบองค์ประกอบเดียว

    iterator erase (iterator position);
  • การลบช่วงขององค์ประกอบ

    iterator erase (iterator first, iterator last);

เนื่องจาก std :: vec.begin () ทำเครื่องหมายจุดเริ่มต้นของคอนเทนเนอร์และถ้าเราต้องการลบองค์ประกอบ ith ในเวกเตอร์ของเราเราสามารถใช้:

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

หากคุณดูอย่างใกล้ชิด vec.begin () เป็นเพียงตัวชี้ไปยังตำแหน่งเริ่มต้นของเวกเตอร์ของเราและการเพิ่มค่าของ i เพื่อเพิ่มตัวชี้ไปยังตำแหน่ง i ดังนั้นเราสามารถเข้าถึงตัวชี้ไปยังองค์ประกอบ ith แทน:

&vec[i]

ดังนั้นเราสามารถเขียน:

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

6
-1 บรรทัดสุดท้ายไม่ได้รวบรวม (อย่างน้อยใน VS2017) รหัสจะถือว่า vector :: iterator นั้นสามารถสร้างได้โดยปริยายจากตัวชี้แบบดิบซึ่งไม่จำเป็นต้องใช้โดยมาตรฐาน
CuriousGeorge

1
นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งสำหรับการดีบักตัววนซ้ำ
Nishant Singh

9

หากคุณมีเวกเตอร์ที่ไม่มีการเรียงลำดับคุณสามารถใช้ประโยชน์จากความจริงที่ว่ามันไม่มีการจัดเรียงและใช้สิ่งที่ฉันเห็นจาก Dan Higgins ที่ 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;
}

เนื่องจากลำดับของรายการไม่สำคัญเพียงนำองค์ประกอบสุดท้ายในรายการและคัดลอกที่ด้านบนสุดของรายการที่คุณต้องการลบจากนั้นป๊อปอัพและลบรายการสุดท้าย


ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดถ้าไม่มีการเรียงลำดับเวกเตอร์ มันไม่ได้ขึ้นอยู่กับข้อสันนิษฐานที่iterator + indexจะให้ตำแหน่งตัววนซ้ำกลับคืนที่ดัชนีนั้นซึ่งไม่เป็นความจริงสำหรับคอนเทนเนอร์ตัววนซ้ำทั้งหมด นอกจากนี้ยังมีความซับซ้อนคงที่แทนที่จะเป็นเชิงเส้นโดยใช้ประโยชน์จากตัวชี้หลัง
theferrit32

1
สิ่งนี้จะต้องถูกเพิ่มเข้าไปใน std lib as unordered_removeและunordered_remove_if…เว้นแต่จะเป็นและฉันพลาดมันไปซึ่งเกิดขึ้นบ่อยขึ้นทุกวัน :)
Will Crawford

หากจะแนะนำให้ใช้การย้ายหรือการมอบหมายแทนการคัดลอกการมอบหมาย
Carsten S

std::removereorders ภาชนะเพื่อให้องค์ประกอบทั้งหมดจะถูกลบออกอยู่ที่ท้ายที่สุดไม่จำเป็นต้องทำด้วยตนเองเช่นนี้ถ้าคุณกำลังใช้ C ++ 17
คี ธ

@keith std::removeช่วยได้อย่างไร cppreference อ้างว่าแม้ใน C ++ 17 removeโอเวอร์โหลดทั้งหมดต้องการเพรดิเคตและไม่มีการทำดัชนี
Paul Du Bois

4

หากคุณทำงานกับเวกเตอร์ขนาดใหญ่ (ขนาด> 100,000) และต้องการลบองค์ประกอบจำนวนมากฉันขอแนะนำให้ทำสิ่งนี้:

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

รหัสใช้ตัวเลขทุกตัวเป็น vec ซึ่งไม่สามารถหารด้วย 3 และคัดลอกไปยัง vec2 หลังจากนั้นก็คัดลอก vec2 ใน vec มันค่อนข้างเร็ว ในการประมวลผลองค์ประกอบ 20,000,000 องค์ประกอบอัลกอริทึมนี้ใช้เวลาเพียง 0.8 วินาที!

ฉันทำสิ่งเดียวกันกับวิธีการลบและมันใช้เวลานานมาก:

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
สิ่งนี้ตอบคำถามได้อย่างไร
Regis Portalez

4
น่าสนใจ แต่ไม่เกี่ยวข้องกับคำถาม!
Roddy

อัลกอริทึมแบบเข้าแทนที่จะเร็วขึ้นหรือไม่
user202729

2
that std :: remove_if (+ ลบ)
RiaD

3

ในการลบองค์ประกอบให้ใช้วิธีดังต่อไปนี้:

// declaring and assigning array1 
std:vector<int> array1 {0,2,3,4};

// erasing the value in the array
array1.erase(array1.begin()+n);

สำหรับภาพรวมที่กว้างขึ้นคุณสามารถเยี่ยมชม: http://www.cplusplus.com/reference/vector/vector/erase/


พิจารณาใช้cppreference ดูนี้ , นี้ฯลฯ
LF

3

ฉันแนะนำให้อ่านเพราะฉันเชื่อว่านั่นคือสิ่งที่คุณกำลังมองหา https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

ถ้าคุณใช้เป็นตัวอย่าง

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

คุณจะลบองค์ประกอบที่ n ของเวกเตอร์ แต่เมื่อคุณลบองค์ประกอบที่สององค์ประกอบอื่น ๆ ของเวกเตอร์จะถูกเลื่อนและขนาดเวกเตอร์จะเป็น -1 นี่อาจเป็นปัญหาหากคุณวนซ้ำผ่านเวกเตอร์เนื่องจากขนาดเวกเตอร์ () ลดลง หากคุณมีปัญหาเช่นนี้ให้ลิงค์แนะนำให้ใช้อัลกอริทึมที่มีอยู่ในห้องสมุด C ++ มาตรฐาน และ "ลบ" หรือ "remove_if"

หวังว่านี่จะช่วยได้


0

คำตอบก่อนหน้านี้คิดว่าคุณมักจะมีดัชนีลงนาม น่าเศร้าที่std::vectorใช้size_typeสำหรับการจัดทำดัชนีและdifference_typeเลขคณิตตัววนซ้ำดังนั้นจึงไม่ทำงานร่วมกันหากคุณเปิดใช้งาน "-Wconversion" และเพื่อน นี่เป็นอีกวิธีหนึ่งในการตอบคำถามในขณะที่สามารถจัดการได้ทั้งที่ลงชื่อและไม่ได้ลงชื่อ:

เพื่อลบ:

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

ที่จะใช้:

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

นี่คืออีกวิธีหนึ่งในการทำเช่นนี้หากคุณต้องการลบองค์ประกอบโดยการค้นหาสิ่งนี้ด้วยค่าของมันในเวกเตอร์คุณเพียงแค่ต้องทำสิ่งนี้บนเวกเตอร์

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

มันจะลบค่าของคุณจากที่นี่ ขอบคุณ


0

แล้วเรื่องนี้ล่ะ

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

วิธีที่เร็วที่สุด (สำหรับการแข่งขันการเขียนโปรแกรมตามเวลาที่ซับซ้อน () = คงที่)

สามารถลบรายการ 100M ใน 1 วินาที

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

และวิธีที่อ่านได้มากที่สุด: vec.erase(vec.begin() + pos);


2
นี่ไม่ใช่แบบพกพามาก มันจะทำงานกับ libstdc ++ แต่ไม่ใช่ libc ++ และไม่ใช่กับ MSVC vector<int>::iteratorไม่จำเป็นต้องเหมือนกับint *
มาร์แชลล์โคลว์

2
มันน่าขยะแขยงฉันคิดว่าฉันจะเปลี่ยน libstdc ++ เพื่อหยุดการทำงาน
Jonathan Wakely
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.