特定の値を持つstlベクトルからアイテムを削除するにはどうすればよいですか?


145

私はstl vectorのAPIドキュメントを見ていて、vectorクラスに特定の値を持つ要素の削除を許可するメソッドがないことに気付きました。これは一般的な操作のようであり、これを行うための組み込みの方法がないのは奇妙に思われます。


2
これについては何度か言及しましたが、Scott Meyerの著書「Effective STL」では、これらの問題を明確にカバーしています。
ロブ・ウェルズ


これはあなたにとって興味深い読書かもしれません: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
このオールインワンステートメント形式は、コンパイラが引数を評価する順序に依存しますか、それともvec.end()、呼び出しのどちらの側でも同じであることが保証されていstd::removeますか?このようにWebの他の部分を読むことは私には安全だと思われますが、それは明確に述べられるべきです。
dmckee ---元モデレーターの子猫

1
大丈夫だよ。の結果はvec.end()同じである必要はありません。正確である必要があります(それが正しい)。
ジムバック

8
vec.end()同じである必要はありstd::removeませんが、変更しないので問題ありません。変更した場合(および古い値が無効になる場合)、問題が発生します。パラメーターの評価順序が指定されていないため、2番目のパラメーターがvec.end()使用されるまでに2番目のパラメーターがまだ有効かどうかがわかりません。同じ理由は単純std::removeで、コンテナのサイズを変更せず、コンテンツを移動するだけです。
Steve Jessop、2013年

2
同じ問題があるので、この質問は重要だと思いました。しかし、私のビジュアルスタジオはstd::remove1つの引数しか取りません。それはconst char *_Filenameです。どのメソッドを呼び出す必要がありますか?
ビクター

15
それはremoveファイルを削除するバージョンです。コンテナを扱う<algorithm>のバージョンにアクセスするには、を含める必要がありますremove
ジムバック

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を開始イテレータと終了イテレータで使用し、次に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、2015年

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、2014

bind2ndが-(C ++ 11で非推奨)であるという事実はどうですか(C ++ 17で削除されました)
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

特にアイテムを消去するために使用できる方法は2つあります。ベクトルを取ることができます

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)効率的な方法(推奨)ERASE-REMOVEイディオムとも呼ばれます

  • std :: removeは、指定された範囲を、指定された要素と等しくないすべての要素がコンテナの先頭にシフトされた範囲に変換します。
  • したがって、実際には一致した要素を削除しないでください。一致しないものを開始にシフトし、イテレータに新しい有効な終了を与えます。O(n)の複雑さが必要です。

削除アルゴリズムの出力は次のとおりです。

10 20 30 50 40 50 

removeの戻り値の型は、その範囲の新しい終わりへの反復子であるためです。

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.