【问题标题】:C++, fast remove elements from vector unique to another vectorC ++,从向量中快速删除另一个向量唯一的元素
【发布时间】:2012-01-23 22:43:20
【问题描述】:

有 2 个未排序的 int 向量和成对的向量 int, int

std::vector <int> v1;
std::vector <std::pair<int, float> > v2;

包含数百万个项目。

如何尽快从 v1 中删除此类项目,这些项目是 v2.first 独有的(即不包含在 v2.first 中)?

例子:

v1:  5 3 2 4 7 8
v2: {2,8} {7,10} {5,0} {8,9}
----------------------------
v1: 3 4

【问题讨论】:

  • 对其中一个向量进行排序是一种选择,还是必须保持该顺序?
  • v1 或 v2 是否比另一个大得多?
  • @Rob:它们的长度大致相同(+-30%)。

标签: c++ vector unique


【解决方案1】:

我会使用两个技巧来尽快做到这一点:

  1. 使用某种关联容器(可能是std::unordered_set)来存储第二个向量中的所有整数,以显着提高查找第一个向量中的某个整数是否应该被删除的效率。

  2. 优化从初始向量中删除元素的方式。

更具体地说,我会执行以下操作。首先创建一个std::unordered_set,然后将第二个向量中的第一个整数的所有整数相加。这给了(预期的)O(1) 查找时间来检查集合中是否存在特定的int

既然您已经这样做了,请使用std::remove_if 算法从哈希表中存在的原始vector 中删除所有内容。您可以使用 lambda 来执行此操作:

std::unordered_set<int> toRemove = /* ... */
v1.erase(std::remove_if(v1.begin(), v1.end(), [&toRemove] (int x) -> bool {
    return toRemove.find(x) != toRemove.end();
}, v1.end());

将所有内容存储在unordered_set 中的第一步需要预期的 O(n) 时间。第二步通过将所有删除聚集到最后并使查找花费很短的时间来完成预期的 O(n) 工作。这为整个过程提供了总共预期的 O(n) 时间、O(n) 空间。

如果允许您对第二个向量(对)进行排序,那么您也可以在 O(n log n) 最坏情况时间、O(log n) 最坏情况空间中执行此操作,方法是按键,然后使用std::binary_search 检查是否应该消除第一个vector 中的特定int。每个二分查找需要 O(log n) 时间,因此排序所需的总时间是 O(n log n),第一个向量中每个元素的 O(log n) 时间(总共 O(n log n) ),删除时间为 O(n),总共为 O(n log n)。

希望这会有所帮助!

【讨论】:

  • 你的算法不就是对std::remove_if()的大描述吗?
  • @AndreCaron- 啊,是的。让 remove_if 删除存储在哈希表中的东西的复杂性曾经是疯狂的,因为没有 lambda,但现在有 到目前为止 更好的方法来做到这一点。让我记下...
  • @templatetypedef 非常有趣的解决方案,有深入的解释,谢谢。但我对 lambda 表达式没有任何经验。 VS 2010 中是否有对 lambda 表达式的编译器支持?可以请您提供代码示例吗?
  • @templatetypedef 有没有没有lambda表达式的解决方案?
  • @justik:是的,使用函数对象。它在语义上完全等效,只是打字时间长一点。
【解决方案2】:

假设两个容器都没有排序并且排序实际上太昂贵或内存不足:

v1.erase(std::remove_if(v1.begin(), v1.end(), 
                        [&v2](int i) { 
                         return std::find_if(v2.begin(), v2.end(), 
                                             [](const std::pair<int, float>& p) { 
                                                return p.first == i; }) 
                                != v2.end() }), v1.end());

或者在first 上对v2 进行排序,并改用二分搜索。如果有足够的内存使用unordered_setfirstv2 进行排序。

完整的C++03版本:

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>

struct find_func {
  find_func(int i) : i(i) {}

  int i;
  bool operator()(const std::pair<int, float>& p) {
    return p.first == i;
  }
};

struct remove_func {
  remove_func(std::vector< std::pair<int, float> >* v2) 
  : v2(v2) {}
  std::vector< std::pair<int, float> >* v2;
  bool operator()(int i) {
    return std::find_if(v2->begin(), v2->end(), find_func(i)) != v2->end();
  }
};


int main()
{
  // c++11 here
  std::vector<int> v1 = {5, 3, 2, 4, 7, 8};
  std::vector< std::pair<int, float> > v2 = {{2,8}, {7,10}, {5,0}, {8,9}};
  v1.erase(std::remove_if(v1.begin(), v1.end(), remove_func(&v2)), v1.end());

  // and here
  for(auto x : v1) {
    std::cout << x << std::endl;
  }

  return 0;
}

【讨论】:

  • 这是 O(n^2),对于数百万个项目来说,速度会非常慢。
  • @interjay 这就是为什么我补充说我说:排序太昂贵而且内存稀缺。有时,构建一个内存开销超过普通数组的数据结构是不可能的,我也提供了更快的解决方案。
  • @pmr 谢谢,有没有没有 lambda 表达式的解决方案?
  • @justik 只需将 lambda 转换为具有一些闭包模拟的函子即可。我会破解它。
  • @pmr 不幸的是,我对 lambda 表达式没有任何经验 :-(.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-21
相关资源
最近更新 更多