如何根据另一个向量中的条件从向量中删除元素?

时间:2016-02-09 08:05:20

标签: c++ stl

我有两个相等长度的向量,我想根据其中一个向量中的条件从中删除元素。应对两者应用相同的删除操作,以使索引匹配。

我使用std::erase提出了一个解决方案,但速度极慢:

vector<myClass> a = ...;
vector<otherClass> b = ...;
assert(a.size() == b.size());
for(size_t i=0; i<a.size(); i++)
{
    if( !a[i].alive() )
    {

        a.erase(a.begin() + i);
        b.erase(b.begin() + i);
        i--;
    }
}

有没有办法可以更有效地执行此操作,最好使用stl算法?

7 个答案:

答案 0 :(得分:8)

如果顺序并不重要,你可以swap向量背面的元素并弹出它们。

for(size_t i=0; i<a.size();)
{
    if( !a[i].alive() )
    {
        std::swap(a[i], a.back());
        a.pop_back();
        std::swap(b[i], b.back());
        b.pop_back();
    }
    else
      ++i;
}

如果您必须维护订单,则可以使用std::remove_if。请参阅this answer如何获取remove谓词中的dereferenced元素的索引:

a.erase(remove_if(begin(a), end(a),
        [b&](const myClass& d) { return b[&d - &*begin(a)].alive(); }),
        end(a));

b.erase(remove_if(begin(b), end(b), 
        [](const otherClass& d) { return d.alive(); }), 
        end(b));

答案 1 :(得分:0)

它缓慢的原因可能是由于O(n ^ 2)的复杂性。为什么不使用列表呢?制作一对a和b也是一个好主意。

答案 2 :(得分:0)

快速获胜将是向后运行循环:即从向量的末尾开始。这倾向于最小化由于元素移除而导致的向后移位的数量。

另一种方法是考虑std::vector<std::unique_ptr<myClass>>等:那么你将基本上移动指针而不是值。

答案 3 :(得分:0)

我建议你最后创建2个新的向量,保留内存和交换向量内容。

vector<myClass> a = ...;
vector<otherClass> b = ...;
vector<myClass> new_a; 
vector<myClass> new_b;
new_a.reserve(a.size());
new_b.reserve(b.size());
assert(a.size() == b.size());
for(size_t i=0; i<a.size(); i++)
{
    if( a[i].alive() )
    {
        new_a.push_back(a[i]);
        new_b.push_back(b[i]);
    }
}
swap(a, new_a);
swap(b, new_b);

它可以消耗内存,但应该快速工作。

答案 4 :(得分:0)

从矢量中间删除很慢,因为它需要在删除点之后重新洗牌。考虑使用另一个容器,这样可以更快地进行擦除。这取决于你的用例,你会经常迭代吗?数据需要按顺序排列吗?如果您不经常迭代,请考虑一个列表。如果您需要维护订单,请考虑一组。如果你经常迭代并且需要维护顺序,根据元素的数量,将所有活元素推回到新向量并将a / b设置为指向它可能会更快。

此外,由于数据是内在联系的,因此在一对或小结构中只包含一个包含数据a和b的向量似乎是有意义的。

答案 5 :(得分:0)

出于性能原因需要使用下一个。 使用

dp

如@Basheba和std :: sort。 使用特殊形式的std :: sort和比较谓词。并且不要从0到n进行枚举。请改用std :: lower_bound,因为将对矢量进行排序。插入元素就像在这个问题中说CashCow:&#34; how do you insert the value in a sorted vector?&#34;

答案 6 :(得分:0)

我有一个类似的问题,我有两个:

std::<Eigen::Vector3d> points;
std::<Eigen::Vector3d> colors;

对于 Open3D 中的 3D 点云,在移除地板后,如果点的 z 坐标大于 0.05,我想删除所有点和颜色。我最终根据索引覆盖了点,然后调整了向量的大小。

bool invert = true;
std::vector<bool> mask = std::vector<bool>(points.size(), invert);
size_t pos = 0;
for (auto & point : points) {
    if (point(2) < CONSTANTS::FLOOR_HEIGHT) {
        mask.at(pos) = false;
    }
    ++pos;
}
size_t counter = 0;
for (size_t i = 0; i < points.size(); i++) {
    if (mask[i]) {
        points.at(counter) = points.at(i);
        colors.at(counter) = colors.at(i);
        ++counter;
    }
}
points.resize(counter);
colors.resize(counter);

这保持了秩序,至少在我的情况下,其工作速度几乎是公认答案中的 remove_if 方法的两倍: 对于 921600 点,运行时间为:

  • 接受答案需要 33 毫秒
  • 此方法为 17 毫秒。