【问题标题】:Delete from specific indexes in a std::vector [duplicate]从 std::vector 中的特定索引中删除 [重复]
【发布时间】:2013-12-14 11:09:49
【问题描述】:

假设我有一个包含 5 个元素的 std::vector,我需要从索引 1 和 3 中删除元素,这将是最快的方法。标准库中是否有任何辅助方法可以为我做到这一点?

【问题讨论】:

  • 擦除将如何帮助我。我的意思是索引 1 和 3 不是一个接一个

标签: c++ vector


【解决方案1】:

您可以使用erase 函数。对于这种特定情况,您提到以下内容:

myvector.erase (myvector.begin()+3);
myvector.erase (myvector.begin()+1);

会成功的。您必须为 erase 函数提供一个迭代器,我建议您阅读文档以了解它的使用。以上应该适用于您的情况。请注意,每次调用 erase 都会在删除位置之后更改剩余元素的索引,因为内部数组元素将相对于删除的项目进行调整。

针对您的评论,您一次只能擦除一个元素,除非它们是连续索引,在这种情况下,您可以使用基于范围的擦除版本来获取开始和结束迭代器。 例如,如果您想删除索引 1,2 和 3,请使用

myvector.erase (myvector.begin()+1,myvector.begin()+4);

正如我已经提到的,在您擦除后的项目索引将相应地降低。这是不可避免的,因为数组中不能有“间隙”。

【讨论】:

  • would'nt myvector.erase (myvector.begin()+3); 更改其他元素的索引。如果我有 6 个元素并想删除 1 ,3 和 5 怎么办?
  • @MistyD 然后你擦除 5 然后 3 然后 1。只有擦除后的索引会受到影响,所以按相反的索引顺序。
【解决方案2】:

这应该是一个相当有效的实现,使用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 });

【讨论】:

  • 巨大的性能提升。非常感谢!
  • 在 VS2017 中,它不会在顶部的 size_type 声明上编译。
【解决方案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());
}

【讨论】:

  • +1 我打算写同样的东西。有些人不喜欢它,因为remove_if 的函子在概念上不是谓词(它的返回值不依赖于它的输入),而且因为remove_if 并没有官方保证按顺序迭代。
  • @jrok:拍,不保证按顺序迭代??我不知道...
  • 嗯,所需的迭代器类别是ForwardIterator,如果这是它的实际类别,它别无选择,只能按顺序进行。但是算法可以针对不同的迭代器类别进行内部专门化(我们之前曾讨论过remove_if,这就是我记得的结论)。
  • @jrok:很有趣。我想即使使用前向迭代器也不能保证对吗?据我所知,它可能会执行索引 1, 0, 3, 2, 5, 4, 7, 6, ... ...
  • 正如其他人指出的那样,这在我的测试中不会返回正确的结果(剩余的一些元素),但它比更正确的结果快约 10 倍,比莫斯科代码中的 Vlad 快 100 倍.
【解决方案4】:

如果你想擦除向量中的一些任意元素,你可以通过以下方式做到这一点

for ( int i : { 3, 1 } ) v.erase( std::next( v.begin(), i ) );

考虑到初始化列表中的项目应该按降序排列。

【讨论】:

  • 我喜欢你的回答,但不喜欢你的格式;)
  • 这是 C++11 还是与 c++98 一起工作?
  • 它只适用于 C++ 2011。
  • 优雅......
  • 这至少比其他方法慢 10 倍(在我的测试中),但确实返回了正确的结果。
猜你喜欢
  • 2010-10-26
  • 2011-07-20
  • 2020-12-26
  • 1970-01-01
  • 2016-09-23
  • 2011-05-25
  • 1970-01-01
  • 2015-10-21
相关资源
最近更新 更多