【问题标题】:C++ Partition a vector of vectors using <algorithm>C++ 使用 <algorithm> 对向量的向量进行分区
【发布时间】:2020-07-14 10:31:50
【问题描述】:

假设你有一个如下定义的二维向量:

std::vector<vector<int>> v

其中代表一个矩阵:

1 1 0 1 3
0 4 6 0 1
5 0 0 3 0
6 3 0 2 5

我想稳定分区(比如用谓词 el != 0)这个矩阵,但在所有方向。这意味着我希望能够得到:

1 1 6 1 3     0 0 0 0 0      1 1 1 3 0     0 1 1 1 3
5 4 0 3 1     1 1 0 1 3      4 6 1 0 0     0 0 4 6 1
6 3 0 2 5     5 4 0 3 1      5 3 0 0 0     0 0 0 5 3
0 0 0 0 0     6 3 6 2 5      6 3 2 5 0     0 6 3 2 5
 (down)         (up)          (right)       (left)

对于两个方向,这可以通过迭代外部向量并分割内部向量(按顺序或反向)非常简单地完成。但是对于其他方向,我不知道如何去做。

有没有办法使用std::stable_partition 来实现这一点。是否有另一种数据结构(支持像向量这样的索引)可以让我更轻松地做到这一点?
如果我让我从头开始实施,是否有标准或推荐的方法来做到这一点?

【问题讨论】:

  • 你可以用向量来做到这一点,它只是不会超级高效,因为数据在内存中不相邻。链表数据矩阵可能也好不了多少。如果您可以用一维数据和隐式行或列大小表示您的二维矩阵,它可能会更好,但您需要进行基准测试。
  • 我可能会为 2d 向量编写列迭代器,然后使用标准算法(对于行,您可以使用标准算法轻松划分内部向量),相反,您只需要反向迭代器
  • @idclev463035818 我想你的意思是正确的迭代器并以某种方式重载 ++ 运算符成为 it = it + col_nb ...我不确定我该怎么做,你能解释一下吗?
  • @AndyG 效率并不是一个真正的大问题,矩阵会相对较小。我只想找到最简单、最清晰的方法。最好不必从头开始编写 stable_partition 实现。
  • 迭代器需要的不仅仅是++ 运算符,不幸的是,编写自己的迭代器需要大量样板文件。也许我会试一试并发布答案......

标签: c++ algorithm c++14 data-partitioning


【解决方案1】:

您无需编写自己的算法实现。当您想将现有算法用于自定义数据结构时,迭代器是自定义点。不幸的是,编写自己的迭代器需要相当多的样板文件。我认为 boost 可以提供帮助,但是如果您想继续使用标准库提供的功能,据我所知,您无法自己编写它。

以下内容需谨慎对待。我假设所有内部向量的大小相同。我不考虑const_iterators,因为你不需要他们使用std::stable_partition。我省略了一些您必须自己添加的成员函数。该算法要求迭代器遵守两个命名概念,即LegacyBidirectionalIteratorValueSwappable。话虽如此,以下是如何实现一个迭代器,使您能够迭代 2d 向量的列:

#include <iostream>
#include <vector>

struct vector2d_col_iterator {
    using container_t = std::vector<std::vector<int>>;
    container_t& container;
    size_t row;
    size_t col;
    vector2d_col_iterator& operator++(){
        ++row;
        return *this;
    }
    bool operator==(const vector2d_col_iterator& other) const {
        return col == other.col && row == other.row;
    }
    bool operator !=(const vector2d_col_iterator& other) const {
        return !(*this == other);
    }
    int& operator*() { return container[row][col]; }
    static vector2d_col_iterator begin(container_t& container,int col) {
        return {container,0,col};
    }
    static vector2d_col_iterator end(container_t& container,int col) {
        return {container,container.size(),col};
    }
};

int main() {
    std::vector<std::vector<int>> v{ {1,2,3},{4,5,6}};
    auto begin = vector2d_col_iterator::begin(v,1);
    auto end = vector2d_col_iterator::end(v,1);
    for ( ; begin != end; ++begin) std::cout << *begin << " ";
}

输出:

2 5

Live example

效率并不是一个真正的大问题,矩阵会相对较小。我只想找到最简单、最清晰的方法。最好不必从头开始编写 stable_partition 实现。

如果矩阵真的很小(比如说 ~20x20 元素)并且效率真的不是问题,那么最简单的可能是仅在内部向量上使用 std::stable_partition。您可以转置矩阵,在循环中为所有内部向量调用算法,再次转置。完毕。那基本上是~10行代码。你的选择;)

【讨论】:

    【解决方案2】:

    使用range-v3,您可以这样做:

    const std::vector<std::vector<int>> v = /*..*/;
    auto is_zero = [](int e){ return e == 0; };
    auto right = v;
    
    for (auto& row : right) {
        ranges::stable_partition(row | ranges::view::reverse, is_zero);
    }
    print(right);
    
    auto top = v;
    for (std::size_t i = 0; i != v[0].size(); ++i) {
        auto col_view = top | ranges::view::transform([i](auto& row)-> int& { return row[i]; });    
    
        ranges::stable_partition(col_view, is_zero);
    }
    print(top);   
    

    Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-12
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多