创建std :: vector减去一个元素的副本的最快方法

fastest way to create a copy of a std::vector minus one element

本文关键字:一个 元素 副本 方法 vector std 创建      更新时间:2023-10-16

我有一个std :: vector [1,2,3,4,5],我想获得另一个载体,其中包含所有元素,但第二个元素:[1,3,3,4,5]。一种方法是(VEC1是我的输入向量):

std::vector<int> vec2;
vec2 = vec1;
vec2.erase(vec2.begin()+1)

在这里,我真的不喜欢o(n)复杂性的擦除,因此考虑到阵列的副本,我将拥有2N操作。我在想旧的虚拟方法会更好:

std::vector<int> vec2;
for(int i=0; i<vec1.size(); ++i){
    if (i != 1) 
        vec2.push_back(vec1[i]);
}

这是摊销的O(n)时间。渐近行为是相同的,但操作数量可能较小。

我必须在相当小的矢量(约100个元素)上执行此操作,但是我有数十亿。我会注意到有很大的区别吗?

您将如何做?

关于复杂性,您不能比O(n)更好,因为您必须复制n个元素。但是出于某些微观化的目的,您可以:

  • 保留先验的规模,如评论
  • 避免通过连续2个副本进行循环内检查条件,但没有检查:

    std::vector<int> vec1 = { 1,2,3,4,5 };
    std::vector<int> vec2(vec1.size()-1);
    constexpr int i = 1; // let i be the position you want to skip
    std::copy(vec1.cbegin(), vec1.cbegin() + i, vec2.begin());
    std::copy(vec1.cbegin() + i+1, vec1.cend(), vec2.begin()+i);
    

由于没有if语句或std::copy-if,这些副本将很简单,而且编译器优化的良好空间。

编辑:如下评论中,调整向量的调整会引起一些初始化的其他初始化,请参阅此处避免启动的方法的讨论:使用vector&lt; char&gt;作为缓冲区,而无需在resize()

上初始化它

可以使用std::vector的现有例程完成。给定i,这是您要跳过的位置(在vec的范围内):

template<typename T>
vector<T> skip_copy(const vector<T> &vec, size_t i)
{
  vector<T> ret;
  ret.reserve(vec.size() - 1);
  ret.insert(ret.end(), vec.begin(), vec.begin() + i);
  ret.insert(ret.end(), vec.begin() + i + 1, vec.end());
  return ret;
}

通过保留空间,我们避免了ret中的任何不必要的内存分配。和vector::insert在最后完成时,无需转移元素。

授予,insert可能会做一些额外的条件,而不是严格需要的(检查迭代器范围是否适合现有存储中),但是一系列push_back调用的速度不太可能更快。并且如果vector的大小很重要,则不必预先确定数组的付费。

这是我想到的最快方法:

std::vector<int> vec1{1, 2, 3, 4, 5};
// Do not call the constructor with the known size here. That would allocate
// AND initialize a lot of data that will be overwritten later anyway.
std::vector<int> vec2;
// Instead, use it on 'reserve' since it only allocates the memory and
// leaves it uninitialized. The vector is still considered empty, but its
// underlying capacity will be big enough to be filled with new data
// without any unnecessary initializations and reallocations occuring (as long
// as you don't exceed the capacity)
vec2.reserve(vec1.size() - 1);
// Nothing expensive should be going on behind the scenes now, just fill it up
for(auto it = vec1.begin(), skip = vec1.begin() + 1 ; it != vec1.end() ; ++it) {
    if(it != skip) {
        vec2.push_back(*it);
    }
}