有条件地将一个矢量复制到另一个矢量的最快方法

时间:2014-07-22 08:55:59

标签: c++ vector stl copy

此问题与现有问题有关:fast way to copy one vector into another

我有一个矢量源矢量S,我想创建一个目标矢量D,它只有满足特定条件的S元素(比如元素是偶数)。注意,源向量是常量向量。

我可以想到两个STL算法来做到这一点:

  • copy_if
  • 的remove_if

在这两种方法中,我都需要确保目标矢量D的大小足够大。因此,我需要创建与S大小相同的初始向量D.此外,在两种方法中,我想将向量D压缩为与其中元素数量相同的长度。我不知道哪一个更快或更方便但我不知道有条件地复制矢量的更好方法吗?

4 个答案:

答案 0 :(得分:6)

最简单的方法是:

auto const predicate = [](int const value) { return value % 2 == 0; };
std::copy_if(begin(src), end(src), back_inserter(dest), predicate);

依赖于push_back

现在,确实,这可能会触发内存重新分配。不过,我想强调push_back 摊销常数的复杂性,这意味着在平均中它是O(1),这是通过拥有指数增长行为(以便执行的分配数量为O(log N))。

另一方面,如果你有100万个元素,其中只有5个是偶数,它不会预先分配4MB的内存,只能在以后只放弃20个字节。

因此:

  • 当分布偏向奇数时,它是最优的,因为它不会过度分配
  • 它接近最佳,否则,因为它没有重新分配

更有趣的是,如果你预先有想法的发布,你可以使用resizeshrink_to_fit

// 90% of the time, 30% of the numbers are even:
dest.reserve(src.size() * 10 / 3);

auto const predicate = [](int const value) { return value % 2 == 0; };
std::copy_if(begin(src), end(src), back_inserter(dest), predicate);

dest.shrink_to_fit();

这样:

  • 如果少于30%,shrink_to_fit 可能修剪多余的
  • 如果有30%,bingo
  • 如果超过30%,则根据需要触发重新分配,仍然遵循O(log N)模式

个人经验告诉我,对reserve的呼吁很少(如果有的话)是值得的,摊销不变的复杂性非常有利于降低成本。

注意:shrink_to_fit是非约束性的,没有保证让capacity等于size的方法,实现选择了什么' s最好的。

答案 1 :(得分:5)

好吧,你可以使用back_inserter:

std::vector<int> foo = {...whatever...};
std::vector<int> bar;
std::back_insert_iterator< std::vector<int> > back_it (bar);

std::copy_if (foo.begin(), foo.end(), back_it, MyPredicate);

或count元素:

std::vector<int> foo = {...whatever...};
int mycount = count_if (foo.begin(), foo.end(), MyPredicate);
std::vector<int> bar (mycount);

std::copy_if (foo.begin(), foo.end(), bar.begin(), MyPredicate );

第三种解决方案:

std::vector<int> foo = {...whatever...};
std::vector<int> bar (foo.size());

auto it = std::copy_if (foo.begin(), foo.end(), bar.begin(), MyPredicate );
bar.resize(std::distance(bar.begin(),it));

答案 2 :(得分:1)

copy_ifremove_if具有不同的语义。前者需要单独的目标向量用于匹配项

copy_if(begin(src), end(src), back_inserter(dst), myPred());

而后者删除了不匹配的商品,但仍然必须删除(删除 - 擦除习语)

src.erase(remove_if(begin(src), end(src), std::not1(myPred()), end(src));

如果您想拥有单独的目标矢量,则需要

remove_copy_if(begin(src), end(src), back_inserter(dst), std::not1(myPred()));

这应该与copy_if同样昂贵。我会发现它更令人困惑,因为双重否定(如果不是,则删除vs复制if)。

答案 3 :(得分:0)

我个人建议使用copy_if()。关于它的好处是它返回它停止复制的输出迭代器。以下是您提到的偶数案例的例子:

vector<int> src;

// initialize to numbers 1 -> 10
for(int i = 0; i < 10; ++i) {
    src.push_back(i);
}

// set initial size to v1.size()
vector<int> dest(src.size());

// use copy_if
auto it = copy_if(src.begin(), src.end(), dest.begin(), [](int val){
    return val %  2 == 0;
});

dest.resize(dest.end() - it);

这样,您只需要调整一次。