【发布时间】:2013-12-14 11:09:49
【问题描述】:
假设我有一个包含 5 个元素的 std::vector,我需要从索引 1 和 3 中删除元素,这将是最快的方法。标准库中是否有任何辅助方法可以为我做到这一点?
【问题讨论】:
-
擦除将如何帮助我。我的意思是索引 1 和 3 不是一个接一个
假设我有一个包含 5 个元素的 std::vector,我需要从索引 1 和 3 中删除元素,这将是最快的方法。标准库中是否有任何辅助方法可以为我做到这一点?
【问题讨论】:
您可以使用erase 函数。对于这种特定情况,您提到以下内容:
myvector.erase (myvector.begin()+3);
myvector.erase (myvector.begin()+1);
会成功的。您必须为 erase 函数提供一个迭代器,我建议您阅读文档以了解它的使用。以上应该适用于您的情况。请注意,每次调用 erase 都会在删除位置之后更改剩余元素的索引,因为内部数组元素将相对于删除的项目进行调整。
针对您的评论,您一次只能擦除一个元素,除非它们是连续索引,在这种情况下,您可以使用基于范围的擦除版本来获取开始和结束迭代器。 例如,如果您想删除索引 1,2 和 3,请使用
myvector.erase (myvector.begin()+1,myvector.begin()+4);
正如我已经提到的,在您擦除后的项目索引将相应地降低。这是不可避免的,因为数组中不能有“间隙”。
【讨论】:
myvector.erase (myvector.begin()+3); 更改其他元素的索引。如果我有 6 个元素并想删除 1 ,3 和 5 怎么办?
这应该是一个相当有效的实现,使用std::move 并且最多只移动每个元素一次。需要对to_remove中删除的索引进行排序。
template<typename T>
void remove_index(std::vector<T>& vector, const std::vector<int>& to_remove)
{
auto vector_base = vector.begin();
std::vector<T>::size_type down_by = 0;
for (auto iter = to_remove.cbegin();
iter < to_remove.cend();
iter++, down_by++)
{
std::vector<T>::size_type next = (iter + 1 == to_remove.cend()
? vector.size()
: *(iter + 1));
std::move(vector_base + *iter + 1,
vector_base + next,
vector_base + *iter - down_by);
}
vector.resize(vector.size() - to_remove.size());
}
// Usage:
//
// std::vector<std::string> values = { "0", "1", "2", "3", "4", "5"};
// remove_index(values, { 1, 3 });
【讨论】:
这比一个接一个地删除要快得多(尽管在某些情况下仍然可以加快速度):
template<class It>
struct remover
{
size_t *i;
It *begin;
It const *end;
explicit remover(size_t &i, It &begin, It const &end) : i(&i), begin(&begin), end(&end) { }
template<class T>
bool operator()(T const &)
{
size_t &i = *this->i;
It &begin = *this->begin;
It const &end = *this->end;
while (begin != end && *begin < i) /* only necessary in case there are duplicate indices */
{ ++begin; }
bool const b = begin != end && *begin == i;
if (b) { ++begin; }
++i;
return b;
}
};
template<class Container, class IndexIt>
IndexIt remove_indices(Container &items, IndexIt indices_begin, IndexIt const &indices_end)
{
size_t i = 0;
std::sort(indices_begin, indices_end);
items.erase(std::remove_if(items.begin(), items.end(), remover<IndexIt>(i, indices_begin, indices_end)), items.end());
return indices_begin;
}
int main()
{
std::vector<int> items(100);
std::vector<size_t> indices;
indices.push_back(5);
indices.push_back(1);
remove_indices(items, indices.begin(), indices.end());
}
【讨论】:
remove_if 的函子在概念上不是谓词(它的返回值不依赖于它的输入),而且因为remove_if 并没有官方保证按顺序迭代。
ForwardIterator,如果这是它的实际类别,它别无选择,只能按顺序进行。但是算法可以针对不同的迭代器类别进行内部专门化(我们之前曾讨论过remove_if,这就是我记得的结论)。
如果你想擦除向量中的一些任意元素,你可以通过以下方式做到这一点
for ( int i : { 3, 1 } ) v.erase( std::next( v.begin(), i ) );
考虑到初始化列表中的项目应该按降序排列。
【讨论】: