从矢量中删除元素,如果它们也在另一个矢量中

时间:2014-11-30 20:59:43

标签: c++ vector erase

假设我有vector a = {"the", "of"}vector b = {"oranges", "the", "of", "apples"}

我想比较两个向量,并从a中移除b中的元素。这就是我想出的:

for (int i = 0; i < a.size(); i++) {
    for (int j =0; j < b.size(); j++) {
       if (a[i] == b[j]) {
          a.erase(a.begin() + i);
       }
    }
}

但是这个循环并没有删除a中的最后一个元素。怪异!

5 个答案:

答案 0 :(得分:7)

问题是,当你删除a的第一个元素时,索引会从0增加到1.在循环的下一次迭代中,向量的大小为1,满足条件外循环导致它终止。您可以通过简单地使用std::remove_ifstd::find和lambda来避免修复此问题所需的任何技巧。

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

int main()
{
    std::vector<std::string> a{ "the", "of" };
    std::vector<std::string> b{ "oranges", "the", "of", "apples" };

    auto pred = [&b](const std::string& key) ->bool
    {
        return std::find(b.begin(), b.end(), key) != b.end();
    };

    a.erase(std::remove_if(a.begin(), a.end(), pred), a.end());

    std::cout << a.size() << "\n";
}

更好的测试是切换ab的内容。这将删除&#34;&#34;&#34; &#34;&#34;&#34;给你带来&#34; oranges&#34;和&#34;苹果&#34;。

答案 1 :(得分:5)

尝试以下

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cassert>

int main()
{
    std::vector<std::string> a = { "the", "of" };
    std::vector<std::string> b = { "oranges", "the", "of", "apples" };

    for ( auto it = a.begin(); it != a.end(); )
    {
        if ( std::find( b.begin(), b.end(), *it ) != b.end() )
        {
            it = a.erase( it ); 
        }
        else
        {
            ++it;
        }
    }

    assert( a.empty() );
}

当然,如果要对矢量进行排序会更好。

答案 2 :(得分:1)

一般情况下,我建议使用STL已经构建的算法,而不是将矢量的内容“手动”走,并选择性地将其删除。

使用删除删除习语

特别是,要从std::vector中删除满足某些属性的项目,您可以考虑使用 erase-remove惯用法
This Q&A on Stackoverflow讨论了从STL容器中删除项目的一些选项,包括std::vector案例。

您可以在下面找到评论的可编辑代码,live here

#include <algorithm>    // for std::remove_if()
#include <iostream>     // for std::cout, std::endl
#include <string>       // for std::string
#include <vector>       // for std::vector
using namespace std;

void print(const char* name, const vector<string>& v);

int main() 
{
    // Input vectors
    vector<string> a = {"the", "of"};
    vector<string> b = {"oranges", "the", "of", "apples"};

    print("a", a);
    print("b", b);

    // Use the erase-remove idiom
    a.erase(
        remove_if(
            a.begin(), 
            a.end(), 

            // This lambda returns true if current string 's'
            // (from vector 'a') is in vector 'b'. 
            [&b](const string& s) 
            {
                auto it = find(b.begin(), b.end(), s);
                return (it != b.end());
            }
        ), 

        a.end()
    );

    cout << "\nAfter removing:\n";
    print("a", a);
}


void print(const char* name, const vector<string>& v) 
{
    cout << name << " = {";
    bool first = true;
    for (const auto& s : v) 
    {
        if (first) 
        {
            first = false;
            cout << s;
        } 
        else 
        {
            cout << ", " << s;
        }
    }
    cout << "}" << endl;
}

输出:

a = {the, of}
b = {oranges, the, of, apples}

After removing:
a = {}

<强> PS
另请注意this very similar question on Stackoverflow


使用std::set_difference()

另一种方法可以是使用std::set_difference(),例如类似下面的代码,live here (请注意,在这种情况下,根据set_difference()先决条件,输入向量必须已经排序。)

#include <algorithm>    // for std::set_difference(), std::sort()
#include <iostream>     // for std::cout, std::endl
#include <iterator>     // for std::inserter
#include <string>       // for std::string
#include <vector>       // for std::vector
using namespace std;

void print(const char* name, const vector<string>& v);

int main() 
{
    // Input vectors
    vector<string> a = {"the", "of"};
    vector<string> b = {"oranges", "the", "of", "apples"};

    print("a", a);
    print("b", b);

    // Sort the vectors before calling std::set_difference().
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());

    // Resulting difference vector
    vector<string> c;
    set_difference(a.begin(), a.end(),
                   b.begin(), b.end(),
                   inserter(c, c.begin()));

    print("difference(a,b)", c);
}


void print(const char* name, const vector<string>& v) 
{
    cout << name << " = {";
    bool first = true;
    for (const auto& s : v) 
    {
        if (first) 
        {
            first = false;
            cout << s;
        } 
        else 
        {
            cout << ", " << s;
        }
    }
    cout << "}" << endl;
}

答案 3 :(得分:1)

您遇到的问题是由于您正在重复a中的元素,因为您正在对其进行迭代,但不会对此进行补偿。当尝试编写带有擦除的循环时,这是一个常见问题。

如果向量内容的顺序并不重要,并且将结果存储在另一个向量中,那么最好的方法之一就是对两个向量进行排序并调用{{ 1}}。

std::set_difference

#include <algorithm> #include <iterator> #include <string> #include <vector> int main() { std::vector<std::string> a = { "the", "of" }; std::vector<std::string> b = { "oranges", "the", "of", "apples" }; std::vector<std::string> res; std::sort(a.begin(), a.end()); std::sort(b.begin(), b.end()); std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(res)); } 将包含resa以外的所有元素,在这种情况下将为空。

如果订单很重要,或者必须在适当的位置完成,您可以使用擦除删除习惯用法。对于较大的向量,这可能会更慢,因为它不可避免地是O(n ^ 2)算法。

b

如果您碰巧无法访问符合C ++ 11标准的编译器,#include <algorithm> #include <iterator> #include <string> #include <vector> struct Pred { const std::vector<std::string>& filter; Pred(const std::vector<std::string>& x) :filter(x){} bool operator()(const std::string& str) const { return std::find(filter.begin(), filter.end(), str) != filter.end(); } }; int main() { std::vector<std::string> a = { "the", "of" }; std::vector<std::string> b = { "oranges", "the", "of", "apples" }; Pred pred(b); a.erase(std::remove_if(a.begin(), a.end(), pred), a.end()); } 结构应该是lambda相当不错的替代方案。否则,这个lambda将完成这项工作:

Pred

答案 4 :(得分:0)

这是从矢量中删除东西的正确语法:

myvector.erase (myvector.begin()+5);

其次,  擦除它后,此向量的索引将无效。

所以我建议你进行两轮扫描。 第一轮,您标记要删除的元素。 在第二轮,你可以删除它们。

BTW你的算法是O(n ^ 2)时间复杂度。 如果可以,我建议您先对矢量进行排序。然后你可以使用更快的算法来处理它。