【问题标题】:Loop over two vectors, remove elements of 1循环两个向量,删除 1 的元素
【发布时间】:2010-09-26 14:53:49
【问题描述】:

我有以下玩具代码,旨在从向量中删除重复项:

void overlap_removal(vector<int> &vec1, vector<int> &vec2) {
  for (vector<int>::iterator it1 = vec1.begin(); it1 != vec1.end(); ++it1) {
    for (vector<int>::iterator it2 = vec2.begin(); it2 != vec2.end(); ++it2) {
      if ((*it1)*(*it2) < 10) {
        vec1.erase();
      }
    }
  }
}

我在实际代码中进行了稍微复杂的比较,但不想混淆问题。问题是在执行此操作后不可避免地会出现分段错误:我认为这是因为我删除了一个元素,然后继续循环遍历同一个向量。

我怎样才能使代码工作?这甚至是正确的起点吗? 提前致谢

【问题讨论】:

  • 顺便说一句,这段代码是等价的:void overlap_removal(vector&lt;int&gt; vec1, vector&lt;int&gt; vec2) {},除了它不会导致分段错误。
  • 是的,我在复制代码时忘记输入 &。干杯
  • @ybungalobill 告诉他为什么是一样的! :P (您通过 按值 传递您的向量,因此它们被复制并且原件未被修改。您需要通过指针(vector&lt;int&gt; *vec1)或更好地通过引用(vector&lt;int&gt;&amp; vec1 )。
  • 代码现在进行了相应的修改。
  • 另外,经过修改以表明等价不是问题,这只是直接比较。

标签: c++ stl vector iterator erase


【解决方案1】:

试试remove_if

基本思想是你提供一个函数对象,如果应该删除传入的元素,则返回 true:

  class ItemInOtherVectorPred
  {
      const std::vector<int>& otherVec;

      ItemInOtherVectorPred(const std::vector<int>& vec) : otherVec(vec) {}

      // return true if removeVecsElem should be deleted
      bool operator()(const int& removeVecsElem) const
      {
          return (otherVec.find(removeVecsElem) != otherVec.end())
      }
  }

然后你使用该对象的一个​​实例来告诉 remove_if 要从你的向量中删除什么。

  void overlap_removal(vector<int> &vec1, vector<int> &vec2) 
  {
     ItemInOtherVectorPred trueIfItemInOtherVecPred( vec2);
     vector<int>::iterator eraseBeg = 
             std::remove_if( vec1.begin(), vec1.end(), trueIfItemInOtherVecPred);
     vec1.erase(eraseBeg, vec1.end());

  }

【讨论】:

  • 不要忘记将 eraseremove_if 的返回值到向量的 end 以实际删除这些值,而不是将它们留在最后。
  • @sbi 不是索引,而是值类型。
  • 对不起。我想我应该去睡觉了。
【解决方案2】:

如果我想尽可能地保留你的逻辑,我会这样做。

it1 在外循环结束时更新,取决于是否在内循环中找到匹配项。

使用引用传递参数以避免复制输入并确保第一个输入反映更改。

第二个vectorconst

void overlap_removal(vector<int>& vec1, const vector<int>& vec2) {
  for (vector<int>::iterator it1 = vec1.begin(); it1 != vec1.end(); ) {
    bool match(false);
    for (vector<int>::const_iterator it2 = vec2.begin(); it2 != vec2.end(); ++it2) {
      if (*it1 == *it2) {
        match = true;
        break;
      }
    }
    if (match)
    {
      it1 = vec1.erase(it1);
    }
    else
    {
       ++it1;
    }
  }
}

使用 STL 功能有更好的方法来做到这一点,但我看到其他人正在发帖。尽管如此,了解向量方法的工作原理还是很不错的,即使您可以在本例中绕过它们。

【讨论】:

  • @Doug - 当然,我注意到有更好的方法来做到这一点。不过,理解vector::erase 的语义并不会损害 OP。
  • 谢谢。如果有更巧妙的方法,我也会感兴趣。
  • @Doug - np,这是我在愧疚感重创vector循环后所做的编辑。
【解决方案3】:

确实如此。删除元素后,迭代器无效。每次删除元素时都必须创建一个新的迭代器。

【讨论】:

    【解决方案4】:

    如果 vec2 很大或者有很多重复元素(在这个函数中一遍又一遍地无用地扫描),那么对第二个向量进行排序并将其放入(无序)集合中可能会更有效,以降低复杂度。

    #include <vector>
    #include <unordered_set>
    #include <iostream>
    #include <iterator>
    #include <algorithm>
    void overlap_removal(std::vector<int> &v1, const std::vector<int> &v2) 
    {
        std::unordered_set<int> s(v2.begin(), v2.end());
        v1.erase(std::remove_if(v1.begin(), v1.end(),
                                [&s](int i){return s.count(i);}),
                 v1.end());
    }
    int main()
    {
        std::vector<int> v1 = {5,6,3,2,3,5,1,2,1};
        std::vector<int> v2 = {2,3};
        overlap_removal(v1, v2);
        copy(v1.begin(), v1.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << '\n';
    }
    

    或者,保持 C++98

    struct IsInSet {
        const std::set<int>& m_s;
        IsInSet(const std::set<int>& s) : m_s(s) {} 
        bool operator()(int i) const { return m_s.count(i); }
    };
    void overlap_removal(std::vector<int> &v1, const std::vector<int> &v2) 
    {
        std::set<int> s(v2.begin(), v2.end());
        v1.erase( std::remove_if(v1.begin(), v1.end(), IsInSet(s)), v1.end());
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 1970-01-01
      • 2017-03-04
      • 1970-01-01
      • 2022-06-12
      • 2015-02-02
      • 1970-01-01
      • 2021-10-28
      相关资源
      最近更新 更多