迭代多个向量,在n个元素后执行操作

时间:2016-10-10 10:14:37

标签: c++ c++11 vector iterator

我有几个std::vector,我需要一个接一个地迭代它们,以便我可以考虑第一组N个元素,然后第二组N个元素执行自定义操作,并且等等,直到最后一个向量的最后一个元素。

一种可行的方法是将每个向量复制到一个更大的向量中(带有2个输入向量的例子):

void foo(const vector<int>::iterator& it, const vector<int>::iterator& it2)
{
}

void iterate(const vector<int>& a, const vector<int>& b, int n)
{
    vector<int> c = a;
    c.insert(c.end(), b.begin(), b.end());

    int i, j, len = c.size();

    for (i = 0, j = 0; i < len; i++)
    {
        if (i > 0 && i % n == 0)
        {
            // custom operation from c.begin() + j to c.begin() + i
            foo(c.begin() + j, c.begin() + i);
            j = i;
        }
    }

    if (i % n != 0)
    {
        // custom operation from c.begin() + j to c.end()
        foo(c.begin() + j, c.end());
    }
}



vector<int> a(100), b(50);
iterate(a, b, 32);

但是这种方法需要额外的矢量分配,可能很大。有没有办法尽可能快地执行相同的操作,而不需要额外的矢量?

6 个答案:

答案 0 :(得分:2)

你可以使用向量数组而不是声明单独的向量,只需迭代它们。

vector<int> v[n];
vector<int>::iterator it;

for(i = 0; i < n; i++) {
   for(it = v[i].begin(); it < v[i].end(); it++) {
      // operation
   }
}

答案 1 :(得分:2)

为什么不编写自己的迭代器?

迭代器(假设您可以将自己限制为所有可能的迭代器操作的子集),它只是一个提供所需操作的类。在您的情况下,您只需要实现++,==和*。

你可以像这样编写这个迭代器类:

  • 首先编写一个名为VirtualCollection的课程。
  • VirtualCollection添加一个成员,该成员是一个向量,指向要循环的基础数据结构(在您的情况下也是向量)。所有底层数据结构必须具有相同的类型(因此不能在vector上循环,然后是list,...)。让我们为这个答案的其余部分调用这个底层数据结构V。我们称这个成员为m_collections
  • 添加方法addCollection,您可以在其中传递对V的引用。 Push_back指向V
  • m_collections的指针
  • 添加名为iterator的内部类。
    • 添加两个V::iterator(或V::const_iterator)类型的成员。拨打一个m_begin和一个m_end(或使用您自己喜欢的命名方式)。
    • 添加V::iterator类型的成员,名为m_current
  • VirtualCollection添加一个开头,它返回iterator内部类的实例。 m_beginm_end应初始化为m_collections中第一个向量的开头和结尾。 m_current也应初始化为m_begin
  • 实施iterator的*运算符。它应该只返回m_current
  • 的内容
  • 通过将自身传递给方法iterator来实现VirtualCollection::increment的++运算符。此增量方法将递增m_current。如果m_current为m_end,则会在VirtualCollection中的下一个集合的开头和结尾重新初始化所有3个成员。
  • 然后添加一些逻辑来正确处理循环结束和边界情况(集合为空,......)。我把它留作练习; - )

答案 2 :(得分:2)

使用range-v3,只需:

void iterate(const std::vector<int>& a, const std::vector<int>& b, int n)
{
    auto r = ranges::view::concat(a, b) | ranges::view::chunk(n);
    for (const auto& e : r | ranges::view::bounded) {
        foo(e);
    }
}

Demo

使用c ++ 17的for-range。

答案 3 :(得分:0)

您可以构造一个指向输入向量的临时向量,并在循环中使用它们进行操作

// not tested
void iterate(const vector<int>& a, const vector<int>& b, int n)
{
    vector<const vector<int> *> c;
    c.push_back(&a);
    c.push_back(&b);

    for(auto vec : c)
    {
        //operate on (*vec), which are a and b
    }
}

答案 4 :(得分:0)

您不能混合来自两个不同容器的迭代器。因此,当您从第一个容器跳到第二个容器时,这不会起作用。

如果您主要关注的是分配额外的向量,则可以在适当的位置工作,并且仅在从第一个向量到第二个向量时分配临时向量。这会将额外的内存需求减少到n

void iterate(const std::vector<int> &a, const std::vector<int> &b, int n)
{
    int remain = a.size();
    for (int i = 0; remain >= n; remain -= n, i += n) {
        foo(a.begin() + i, a.begin() + i + n);
    }

    if (remain > 0) {
        std::vector<int> tmp;
        tmp.insert(tmp.end(), a.end() - remain, a.end());
        tmp.insert(tmp.end(), b.begin(), b.begin() + n - remain);
        foo(tmp.begin(), tmp.end());
    }

    int start = n - remain;
    remain = b.size() - start;
    for (int i = start; remain >= n; remain -= n, i += n) {
        foo(b.begin() + i, b.begin() + i + n);
    }

    if (remain > 0)
        foo(b.end() - remain, b.end());
}

如果您不关心每次都准确地执行n元素,您也可以只处理一个向量及其余向量,然后处理下一个向量

void iterate(const std::vector<int> &v, int n)
{
    int remain = v.size();
    for (int i = 0; remain >= n; remain -= n, i += n)
        foo(v.begin() + i, v.begin() + i + n);

    if (remain > 0)
        foo(v.end() - remain, v.end());
}

std::vector<int> a(100), b(50);
iterate(a, 32);
iterate(b, 32);

答案 5 :(得分:0)

您可以像这样使用std :: reference_wrapper:

static int val = 1;
void foo(const vector<int>::iterator& it, const vector<int>::iterator& it2)
{
    std::for_each(it, it2, [](int& v) {v = val; });
    val++;
}

void iterate(const vector<std::reference_wrapper<vector<int>>>& data, vector<int>::size_type n)
{
    vector<int>::size_type tmp_beg = 0;
    vector<int>::size_type tmp_end = n;
    for (std::reference_wrapper<vector<int>> vref : data)
    {
        vector<int>& v = vref.get();
        while (tmp_end < v.size())
        {
            foo(v.begin() + tmp_beg, v.begin() + tmp_end);
            tmp_beg = tmp_end;
            if (tmp_beg >= v.size()) {
                tmp_beg -= v.size();
            }
            tmp_end = tmp_beg + n;
        } //tmp_end >= v.size()

        foo(v.begin() + tmp_beg, v.end());

        val--; //We need the same value again....

        tmp_beg = 0;
        tmp_end -= v.size();
        if (tmp_end == 0) {
            tmp_end = n;
        }
    }
}


int main()
{
    vector<int> a(100), b(50);
    iterate({ a,b }, 32);
    return 0;
}