벡터에서 요소 지우기


101

지우기 방법을 사용하여 벡터에서 요소를 지우고 싶습니다. 그러나 여기서 문제는 요소가 벡터에서 한 번만 발생한다는 보장이 없다는 것입니다. 여러 번 나타날 수 있으며 모두 지워야합니다. 내 코드는 다음과 같습니다.

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

이 코드는 벡터를 반복하는 동안 벡터의 끝을 변경하기 때문에 분명히 충돌합니다. 이를 달성하는 가장 좋은 방법은 무엇입니까? 즉, 벡터를 여러 번 반복하거나 벡터의 복사본을 하나 더 만들지 않고도이를 수행 할 수있는 방법이 있습니까?

답변:


167

사용 제거를 / 삭제 관용구 :

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

의 시작 부분에서 remove제거 할 값 ( number_in) 과 다른 요소 를 압축하고 vector해당 범위 이후의 첫 번째 요소에 반복자를 반환합니다. 그런 다음 erase이러한 요소 (값이 지정되지 않음)를 제거합니다.


2
std::remove()제거 할 요소를 덮어 쓰도록 요소를 이동합니다. 알고리즘은 컨테이너의 크기를 변경하지 않으며 n요소가 제거되면 마지막 n요소 가 정의되지 않습니다 .
wilhelmtell

20
erase-remove 관용구는 Scott Meyers의 "Effective STL : 50 Specific Ways to Improve Your Use of the Standard Template Library"책의 항목 32에 설명되어 있습니다.
Alessandro Jacopson

1
@Benjin은 업데이트가 필요하지 않으며 개체가 존재하는 경우 소멸자를 호출합니다.
Motti

54
이와 같은 STL '관용구'는 소규모 프로젝트에 Python을 사용하게합니다.
Johannes Overmann

1
하나의 반복자 과부하를 의미 @LouisDionne, 나는 두 반복자 과부하를 사용하고 있습니다
은 Motti

53

erase를 호출하면 반복기가 무효화되며 다음을 사용할 수 있습니다.

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

}

또는 std :: remove_if를 functor 및 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());

이 경우 자체 functor를 작성하는 대신 std :: remove 사용할 수 있습니다 .

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

C ++ 11에서는 functor 대신 lambda를 사용할 수 있습니다.

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

C ++ 17에서는 std :: experimental :: erasestd :: experimental :: erase_if 도 사용할 수 있으며, C ++ 20에서는 (마지막으로) std :: erasestd :: erase_if 로 이름이 변경되었습니다 .

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

또는:

std::vector<int> myNumbers;
std::erase(myNumbers, number_in);

2
equal_to를 사용할 수 있는데 왜 자신의 펑터를 사용합니까? - P sgi.com/tech/stl/equal_to.html
크리스 광대 - 젊은

3
그건 그렇고, erasewith 호출 remove은 이것을 수행하는 표준 방법입니다.
Konrad Rudolph

1
나는 그가 정확히 그렇게 생각한다. 하지만 자신의 functor iirc를 사용한다면 remove_if를 사용해야합니다. 아니면 그냥 펑없이 제거를 사용
litb - 요하네스 SCHAUB

3
+1 철자 된 코드는 프로그래밍 대회에서 저에게 도움이되었지만 "지우기 제거 관용구 만 사용"은 도움이되지 않았습니다.

13
  1. 인덱스 액세스를 사용하여 반복 할 수 있습니다.

  2. O (n ^ 2) 복잡성을 피하기 위해 두 개의 인덱스, i-현재 테스트 인덱스, j-인덱스를 사용하여 다음 항목을 저장하고주기가 끝날 때 벡터의 새 크기를 사용할 수 있습니다.

암호:

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

이 경우 반복자를 무효화하지 않고 복잡성은 O (n)이며 코드는 매우 간결하며 일부 도우미 클래스를 작성할 필요가 없습니다.

이 코드는 erase방법을 사용하지 않지만 작업을 해결합니다.

pure stl을 사용하면 다음과 같은 방식으로이를 수행 할 수 있습니다 (이는 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());
}

4

이 작업을 수행하는 이유에 따라 std :: set을 사용하는 것이 std :: vector보다 더 나은 아이디어 일 수 있습니다.

각 요소가 한 번만 발생할 수 있습니다. 여러 번 추가하면 어쨌든 지울 인스턴스가 하나만 있습니다. 이렇게하면 지우기 작업이 간단 해집니다. 지우기 작업은 벡터보다 시간 복잡도가 낮지 만 세트에서 요소를 추가하는 것이 더 느리므로 그다지 이점이 없을 수 있습니다.

물론 요소가 벡터에 추가 된 횟수 또는 요소가 추가 된 순서에 관심이있는 경우에는 작동하지 않습니다.


-1

첫 번째 요소를 지우려면 다음을 사용할 수 있습니다.

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

it = mV.begin(); 
mV.erase(it); 

1
그것은 질문이 아니 었습니다. 11 년 후의 답변은 적절하지 않은 것 같습니다, Amit!
날개를 가진 소행성
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.