【问题标题】:How to remove elements in a vector that match some element in another vector如何删除与另一个向量中的某些元素匹配的向量中的元素
【发布时间】:2017-12-07 17:02:35
【问题描述】:

我想删除向量v2 中与另一个向量v1 中的元素匹配的元素,其中v1 中的每个元素在v2 中都有对应的元素,但反之则不然。

我的想法是像这样使用std::vector::erasestd::remove_ifstd::find

std::vector<int> v1{ 6, 2, 4 };
std::vector<int> v2{ 8, 2, 9, 4, 5, 3, 7, 1, 6 };

v2.erase(
  std::remove_if(v2.begin(), v2.end()
  , [&v1](auto& item)
  {
    return std::find(v1.begin(), v1.end(), item) != v1.end();
  })
);

哪个有效。但是,我想知道我是否可以不匹配已经匹配的那些,因为这是从v1 -&gt; v2 开始时的 1:1 关系。 v1之后会被吹走,所以可以修改。

【问题讨论】:

  • 你能先对向量进行排序吗?
  • @UKMonkey,而不是v2。你可以对v1 做任何事情。 v2 不一定是排序的。
  • 如果 v1 不是特别大,您应该将其转换为 std::unordered_set,利用 0(1) 复杂度。

标签: c++ c++11 visual-c++ c++14


【解决方案1】:

排序 v1。使用 std 相等范围查找其中的元素。将它与您的 remove-erase 习语代码一起使用(带有错误修复)。

这是 O((M+N)lg N)

std::sort( v1.begin(), v1.end() );
auto removed=std::remove_if(
  v2.begin(), v2.end(), 
  [&v1](auto& item)
  {
    auto r= std::equal_range(v1.begin(), v1.end(), item);
    return r.first!=r.second;
  }
);
v2.erase(removed, v2.end());

请注意,您在擦除调用中忘记了.end,这是执行 remove-erase 时常见的严重错误。

如果条目数量合理,则编辑源向量以消除元素是不值得的,除非是非常花哨的数据结构。

【讨论】:

  • 噢!是的,错过了那个错误。
  • 嗯,equal_range 不要求对v1 进行排序吗?
  • @Adrian 如果您无法对 v1 进行排序,请在 v1 中创建一个索引向量并根据 v1 的值对其进行排序
  • @UKMonkey,是的,我知道,但没有那一点答案是不完整的。
  • @Adrian 这个答案的第一句话是“sort v1”?
【解决方案2】:

您可以实现线性复杂性来解决您的问题,但需要额外的内存。您可以将第一个向量转移到散列(unordered_set)。因此,您将获得 O(N) 复杂度(不包括创建哈希的努力)

std::vector<int> v1{ 6, 2, 4 };
std::vector<int> v2{ 8, 2, 9, 4, 5, 3, 7, 1, 6 };

std::unordered_set<int> hash_v1( v1.begin(), v1.end() );

// linear complexity for remove_if
v2.erase( std::remove_if( v2.begin(), v2.end(), [&hash_v1] ( auto& item )
{
    return hash_v1.find( item ) != hash_v1.end(); // constant time
}), v2.end());

不要忘记 v2.end() 作为 ease 方法的第二个参数。

【讨论】:

  • 不错。对我的应用程序来说有点矫枉过正。仍然很高兴看到替代品。
【解决方案3】:

如果您被允许修改v1,那么类似以下的内容将起作用:

v2.erase(
  std::remove_if(v2.begin(), v2.end(),
    [&v1](auto& item) {
      auto it = std::find(v1.begin(), v1.end(), item);
      if (it != v1.end()) {
        v1.erase(it);
        return true;
      }
      return false;
    }
  ), v2.end());

编辑 1:首先对 v1 进行排序,然后使用 std::lower_bound 来获得 O(log n) 的搜索速度,而不是 O(n)。

编辑 2:v1 的元素存储到 std::unordered_set 中以进行擦除并找到 O(1) 摊销。

编辑 3: 添加了 Yakk 提到的关于 erase 的错误修正。

【讨论】:

  • 是的,但是擦除是 O(N) 操作。
  • @Adrian 是的,我知道,这只是一个快速而肮脏的解决方案 :)。如果我发现更好的东西,我会相应地更新(除非有人打败我)。您当然可以始终将v1 复制到例如一个unordered_set,这将使删除并找到两个 O(1)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多