특정 값을 가진 stl 벡터에서 항목을 어떻게 제거합니까?


145

stl 벡터에 대한 API 설명서를보고 있었고 벡터 클래스에 특정 값을 가진 요소를 제거 할 수있는 메소드가 없다는 것을 알았습니다. 이것은 일반적인 작업처럼 보이지만이 작업을 수행 할 방법이없는 것이 이상합니다.


2
나는 이것을 여러 번 언급 한 것을 알고 있지만 Scott Meyer의 책 Effective STL 은 이러한 문제점을 분명하게 다루고 있습니다.
Rob Wells


이 흥미로운 당신에게 읽기 수 있습니다 en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
sergiol

답변:


165

std::remove실제로 컨테이너에서 요소를 지우지는 않지만 컨테이너의 끝에 container_type::erase있는 추가 요소를 실제로 제거 하기 위해 전달할 수있는 새로운 끝 반복자를 반환합니다 .

std::vector<int> vec;
// .. put in some values ..
int int_to_remove = n;
vec.erase(std::remove(vec.begin(), vec.end(), int_to_remove), vec.end());

3
이 all-in-one-statement 형식은 컴파일러가 인수를 평가하는 순서에 따라 결정 됩니까? vec.end()또는 호출의 양쪽에서 동일해야합니다 std::remove. 안전한 것처럼 웹의 다른 부분을 읽을 때 나에게 보이지만 명확하게 명시해야합니다.
dmckee --- 전 운영자 고양이 새끼

1
괜찮아. 결과가 vec.end()같을 필요는 없습니다. 그것은 정확해야합니다 (그렇습니다).
Jim Buck

8
vec.end()동일해야하지만 std::remove변경하지 않기 때문에 괜찮 습니다. 매개 변수의 평가 순서가 지정되지 않았으므로 두 번째 값 vec.end()이 사용되는 시간까지 여전히 유효한지 알 수 없습니다 . 그것이 같은 이유는 간단 std::remove합니다. 컨테이너의 크기를 변경하지 않고 단지 내용을 이동시킵니다.
Steve Jessop

2
나는 같은 문제가 있기 때문에이 질문이 중요하다는 것을 알았습니다. 그러나 내 비주얼 스튜디오는 std::remove단 하나의 주장 만 취합니다 . 즉 const char *_Filename. 어떤 방법으로 전화해야합니까?
빅터

15
remove파일을 삭제하는 버전입니다 . 컨테이너를 다루는 <algorithm>해당 버전에 액세스하려면 포함해야합니다 remove.
Jim Buck

64

제거 할 경우 항목을, 다음은 좀 더 효율적입니다.

std::vector<int> v;


auto it = std::find(v.begin(), v.end(), 5);
if(it != v.end())
    v.erase(it);

또는 주문이 중요하지 않은 경우 항목을 이동하는 오버 헤드를 피할 수 있습니다.

std::vector<int> v;

auto it = std::find(v.begin(), v.end(), 5);

if (it != v.end()) {
  using std::swap;

  // swap the one to be removed with the last element
  // and remove the item at the end of the container
  // to prevent moving all items after '5' by one
  swap(*it, v.back());
  v.pop_back();
}

3
std :: remove_if 접근 방식과 달리 중복 항목이 있으면 제거되지 않습니다.
Den-Jason

15

전역 메소드 std :: remove를 begin 및 end 반복자와 함께 사용하고 std :: vector.erase를 사용하여 실제로 요소를 제거하십시오.

문서 링크
std :: remove http://www.cppreference.com/cppalgorithm/remove.html
std :: vector.erase http://www.cppreference.com/cppvector/erase.html

std::vector<int> v;
v.push_back(1);
v.push_back(2);

//Vector should contain the elements 1, 2

//Find new end iterator
std::vector<int>::iterator newEnd = std::remove(v.begin(), v.end(), 1);

//Erase the "removed" elements.
v.erase(newEnd, v.end());

//Vector should now only contain 2

내 오류를 지적한 Jim Buck에게 감사합니다.


벡터 중간에있는 요소가 지워질 때 다른 요소의 이동을 방지 할 수 있으므로 더 빠릅니다. 먼저 벡터의 끝으로 이동 한 다음 벡터의 끝에서 버릴 수 있습니다.
Etherealone

5

다른 답변은이 작업을 수행하는 방법을 다루고 있지만, 이것이 벡터 API에없는 것이 실제로 이상하지 않다는 것을 지적했습니다. 그것을 제거하기 위해 복사.

이 작업을 집중적으로 수행하는 경우 이러한 이유로 std :: set을 고려해 볼 가치가 있습니다.


5

정렬되지 않은 벡터가있는 경우 마지막 벡터 요소와 간단히 교체 할 수 있습니다 resize().

정렬 된 용기로, 당신은 최상의 떨어져 있습니다 std::vector::erase(). 에 std::remove()정의되어 <algorithm>있지만 실제로는 지우지 않습니다. (문서를주의해서 읽으십시오).



3

에서 C ++ 20 :

멤버가 아닌 함수가 도입 std::erase되어 벡터와 값을 입력으로 제거합니다.

전의:

std::vector<int> v = {90,80,70,60,50};
std::erase(v,50);

2
뿐만 아니라 각 특정 컨테이너에 과부하가 걸렸습니다!
HolyBlackCat

... map::erase! 와 같은 컨테이너 특정 속성을 활용합니다 .
LF

2

술어를 사용할 수 있으려면 std :: remove_if 도 참조하십시오 ...

위 링크의 예는 다음과 같습니다.

vector<int> V;
V.push_back(1);
V.push_back(4);
V.push_back(2);
V.push_back(8);
V.push_back(5);
V.push_back(7);

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 4 2 8 5 7"

vector<int>::iterator new_end = 
    remove_if(V.begin(), V.end(), 
              compose1(bind2nd(equal_to<int>(), 0),
                       bind2nd(modulus<int>(), 2)));
V.erase(new_end, V.end()); [1]

copy(V.begin(), V.end(), ostream_iterator<int>(cout, " "));
    // The output is "1 5 7".

2
이 게시물에 자세한 내용을 추가하십시오. 그것이 의미하는 바와 같이, 콘텐츠의 대부분은 링크에서 나오며 링크가 끊어지면 손실됩니다.
Mick MacCallum

사실 bind2nd에 대한 어떤 것은 - (C ++ 11을 사용되지 않음) (+ + 17 C에서 제거)
Idan Banani

0

추가 포함없이 수행하려면 다음을 수행하십시오.

vector<IComponent*> myComponents; //assume it has items in it already.
void RemoveComponent(IComponent* componentToRemove)
{
    IComponent* juggler;

    if (componentToRemove != NULL)
    {
        for (int currComponentIndex = 0; currComponentIndex < myComponents.size(); currComponentIndex++)
        {
            if (componentToRemove == myComponents[currComponentIndex])
            {
                //Since we don't care about order, swap with the last element, then delete it.
                juggler = myComponents[currComponentIndex];
                myComponents[currComponentIndex] = myComponents[myComponents.size() - 1];
                myComponents[myComponents.size() - 1] = juggler;

                //Remove it from memory and let the vector know too.
                myComponents.pop_back();
                delete juggler;
            }
        }
    }
}

0

특히 항목을 지우는 데 사용할 수있는 두 가지 방법이 있습니다. 벡터를 가지고 가자

std :: vector < int > v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(40);
v.push_back(50);

1) 비효율적 인 방법 : 비록 효율적이지만 삭제 기능이 요소를 삭제하고 모든 요소를 ​​왼쪽으로 1 만큼 이동하기 때문에 그렇지 않습니다. 복잡성이 O (n ^ 2)

std :: vector < int > :: iterator itr = v.begin();
int value = 40;
while ( itr != v.end() )
{
   if(*itr == value)
   { 
      v.erase(itr);
   }
   else
       ++itr;
}

2) 효율적인 방법 (권장) : 삭제 제거 관용구 라고도 합니다.

  • std :: remove는 주어진 범위를 주어진 요소와 같지 않은 모든 요소가 컨테이너의 시작 부분으로 이동 된 범위로 변환합니다.
  • 따라서 실제로 일치하는 요소를 제거하지 마십시오. 그것은 일치하지 않는 것을 시작으로 옮기고 새로운 유효한 끝으로 반복자를 제공합니다. O (n) 복잡성 만 있으면됩니다.

제거 알고리즘의 출력은 다음과 같습니다.

10 20 30 50 40 50 

제거의 리턴 유형은 해당 범위의 새 끝에 대한 반복자입니다.

template <class ForwardIterator, class T>
  ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val);

이제 벡터의 지우기 기능을 사용하여 벡터의 새로운 끝에서 오래된 끝까지 요소를 삭제하십시오. O (1) 시간이 필요합니다.

v.erase ( std :: remove (v.begin() , v.end() , element ) , v.end () );

이 방법은 O (n)에서 작동합니다


0

*

C ++ 커뮤니티가 귀하의 요청을 들었습니다 :)

*

C ++ 20 은 지금 쉬운 방법을 제공합니다. 다음과 같이 간단합니다.

#include <vector>
...
vector<int> cnt{5, 0, 2, 8, 0, 7};
std::erase(cnt, 0);

std :: erasestd :: erase_if를 확인해야합니다 .

값의 모든 요소 (여기서는 '0')를 제거 할뿐만 아니라 O (n) 시간 복잡성으로 수행합니다. 당신이 얻을 수있는 가장 좋은 것입니다.

컴파일러가 C ++ 20을 지원하지 않으면 erase-remove 이디엄 을 사용해야합니다 .

#include <algorithm>
...
vec.erase(std::remove(vec.begin(), vec.end(), 0), vec.end());
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.