从多个std :: vectors中删除项目的最快方法

时间:2019-02-26 10:44:57

标签: c++ stdvector

我有三个std::vectors,每个包含不同的数据类型。

我需要做的是从每个索引项中删除相同的索引项,具体取决于第一个索引项的值。

在下面的代码中,如果localMap_points[i].m_counter的值大于30,则会从所有三个向量中删除索引为[i]的项目。 (localMap_desc包含8个其他矢量项,因此x 8添加到该矢量)

这可以正常工作,但是速度很慢。有更快的方法吗?

我有:

for (int i = 0; i < localMap_points.size(); i++)
{
    if (localMap_points[i].m_counter > 30)
    {
        localMap_kp.erase(localMap_kp.begin() + i, localMap_kp.begin() + i + 1); // Deleting from n element to n element
        localMap_desc.erase(localMap_desc.begin() + (i * 8), localMap_desc.begin() + (i * 8) + 8); // Deleting from n element to n element X 8
        localMap_points.erase(localMap_points.begin() + i, localMap_points.begin() + i + 1); // Deleting from n element to n element

    }

}

3 个答案:

答案 0 :(得分:4)

这里的性能瓶颈是std::vector的内存布局,可能还有特殊的成员函数属性/矢量元素的存在。如果擦除中间的一个元素,则必须移动从该位置到末尾的所有元素,以适应删除的元素之前的元素。这是通过

完成的
  • 如果没有移动ctor或没有标记noexcept的移动ctor,则每个元素一个副本构造
  • 否则每个元素只能移动一次。

因此,要确保的第一件事是向量中存储的元素类型具有noexcept move构造函数。

第二,请确保在此处使用erase-remove idiom。通过遵循这种模式,在进行任何擦除操作之前,首先要完成对交换调用的重新排序。对于要删除的n个项目,它们是n交换调用。然后,您将对std::vector::erase进行微不足道的调用,因为所有元素都已经按需放置。为了使此过程尽快完成,您可能要考虑为自定义类型提供一个swap函数。

答案 1 :(得分:2)

使用更合适的数据结构。向量不用于快速内部删除和插入(O(n)),如果不需要索引访问(插入/删除为O(1) ==恒定时间),则列表可能是更好的选择。根据您的需要,您可以使用辅助结构,但是如果没有更多信息,这是不可能的。

---添加了另一种想法(尽管有人已经提出了这个想法)。

如果矢量的顺序无关紧要,则可以swap将要删除的项目与矢量中的最后一个一起,然后pop_back()

应该是O(1)

---大声思考。

另一个技巧是使用priority queue。它们是一种数据结构,它根据一个字段的“权重”对自身进行重新排序,在您的应用程序中,它将是counter字段。他们可以在O(log n)中插入,并在O(1)中从顶部删除。因此,删除具有较高counter的项目将非常快,因为它们始终位于最前面。

答案 2 :(得分:2)

这是一个示例,您如何一次将erase-remove idiom应用于三个向量。

O( N )中的算法,即要求对向量进行一次遍历。

template<typename T1, typename T2, typename T3, typename P>
void erase3(T1& v1, T2& v2, T3& v3, P predicate) {
    auto first1 = begin(v1);
    auto first2 = begin(v2);
    auto first3 = begin(v3);
    while (first1 != end(v1) && !predicate(*first1)) {
        ++first1; ++first2; ++first3;
    }
    if (first1 != end(v1)) {
        auto it1 = first1; auto it2 = first2; auto it3 = first3;
        while (++it1 != end(v1)) {
            ++it2;
            ++it3;
            if (!predicate(*it1)) {
                *first1++ = std::move(*it1);
                *first2++ = std::move(*it2);
                *first3++ = std::move(*it3);
            }
        }
        v1.erase(first1, end(v1));
        v2.erase(first2, end(v2));
        v3.erase(first3, end(v3));
    }
}

int main()
{
    std::vector<int> v1 = { 1,2,3,4,5 }, v2 = { 11,12,13,14,15 }, v3 = { 21,22,23,24,25 };

    erase3(v1, v2, v3, [](int a) { return a == 3 || a == 4; });

}

这是通过将所有剩余元素移动到向量的开头,然后从结尾处修剪掉移出的元素来实现的。