【问题标题】:remove arbitrary list of items from std::vector<std::vector<T> >从 std::vector<std::vector<T> > 中删除任意项目列表
【发布时间】:2014-05-24 11:57:35
【问题描述】:

我有一个向量向量,代表一个数组。我想有效地删除行,即以最小的复杂性和分配

我考虑过构建一个新的向量向量,仅复制未删除的行,使用移动语义,如下所示:

    //std::vector<std::vector<T> > values is the array to remove rows from
    //std::vector<bool> toBeDeleted contains "marked for deletion" flags for each row

    //Count the new number of remaining rows
    unsigned int newNumRows = 0;
    for(unsigned int i=0;i<numRows();i++)
    {
        if(!toBeDeleted[i])
        {
            newNumRows++;
        }
    }


    //Create a new array already sized in rows
    std::vector<std::vector<T> > newValues(newNumRows);

    //Move rows
    for(unsigned int i=0;i<numRows();i++)
    {
        if(!toBeDeleted[i])
        {
            newValues[i] = std::move(values[i]);
        }
    }

    //Set the new array and clear the old one efficiently
    values = std::move(newValues);

这是最有效的方法吗?

编辑:我只是想我可以通过迭代地向下移动行来避免分配新数组,这可能会更有效,代码也更简单:

    unsigned int newIndex = 0;
    for(unsigned int oldIndex=0;oldIndex<values.size();oldIndex++)
    {
        if(!toBeDeleted[oldIndex])
        {
            if(oldIndex!=newIndex)
            {
                values[newIndex] = std::move(values[oldIndex]);
            }

            newIndex++;
        }
    }
    values.resize(newIndex);

谢谢!

【问题讨论】:

  • 你为什么不直接使用std::remove_if?我严重怀疑您的实现是否更快或使用更少的内存,只需在滚动您自己的实现之前进行分析。如果你不测量,你只是在猜测。
  • 好吧,remove_if 将一个函数作为参数,该函数告诉是否仅根据项目值删除项目。我不能自己标记项目,我只有一个要删除的索引的布尔表。在这里使用 remove_if 不是那么简单
  • 你可以做std::vector&lt;int&gt; vec; std::vector&lt;bool&gt; remVec; auto begin = std::begin(vec); auto end = std::end(vec); size_t idx = 0; std::remove_if(begin,end,[&amp;idx,&amp;remVec](const int&amp; ){return remVec[idx++];});。尽管我建议首先不要使用标志数组。与其设置标志,不如考虑将元素与最后一个仍然完好的元素交换,并维护一个索引,之后所有元素都需要被删除。那么您所要做的就是致电resize 进行任何实际删除。
  • 哦,如果有特定的顺序,您还可以使用 std::vector&lt;std::pair&lt;std::vector&lt;T&gt;,bool&gt;&gt; rows; 来跟踪元素的标志(我不知道为什么我一直重复 std::vector&lt;pair&lt;vector&lt;T&gt;,bool&gt;&gt; rows;不会真正混淆任何人)。

标签: c++ c++11 vector move-semantics erase-remove-idiom


【解决方案1】:

这可以使用通常的erase-remove idiom 的变体来解决,在std::remove_if 中使用一个 lambda,它在要删除的索引的迭代器范围内查找当前行的索引:

#include <algorithm>    // find, remove_if
#include <iostream>
#include <vector>

template<class T>
using M = std::vector<std::vector<T>>; // matrix

template<class T>
std::ostream& operator<<(std::ostream& os, M<T> const& m)
{
    for (auto const& row : m) {
        for (auto const& elem : row)
            os << elem << " ";
        os << "\n";
    }
    return os;
}

template<class T, class IdxIt>
void erase_rows(M<T>& m, IdxIt first, IdxIt last)
{
    m.erase(
        std::remove_if(
            begin(m), end(m), [&](auto& row) {
            auto const row_idx = &row - &m[0];
            return std::find(first, last, row_idx) != last;
        }), 
        end(m)
    );
}

int main()
{
    auto m = M<int> { { 0, 1, 2, 3 }, { 3, 4, 5, 6 }, { 6, 7, 8, 9 }, { 1, 0, 1, 0 } };
    std::cout << m << "\n";

    auto drop = { 1, 3 };
    erase_rows(m, begin(drop), end(drop));

    std::cout << m << "\n";
}

Live Example.

注意:因为从 C++11 开始,std::vector 具有移动语义,因此无论您的 T 类型如何,都可以使用简单的指针操作在您的 std::vector&lt;std::vector&lt;T&gt;&gt; 中移动行但是,如果您想要 column-deletion,那将是完全不同的!)。

【讨论】:

  • 可爱!顺便说一句,你的主目录中的drop 是什么类型的?
  • @Walter 是 std::initializer_list&lt;int&gt;std::begin()std::end() 与各种标准容器的工作方式相同。
  • 你能解释一下auto const row_idx = &amp;row - &amp;m[0];吗?
  • @galinette 是的,它将行索引计算为第一行和当前行地址之间的差异。
  • @TemplateRex : row 和 m[0] 是迭代器,对吧?那么row_idx是迭代器上两个指针的区别??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 2017-09-01
  • 2010-12-04
  • 2019-10-20
  • 2016-03-31
  • 2019-11-07
  • 1970-01-01
相关资源
最近更新 更多