在 C++ 中重塑二维向量

Posted

技术标签:

【中文标题】在 C++ 中重塑二维向量【英文标题】:Reshape 2D Vector in C++ 【发布时间】:2017-05-17 20:46:22 【问题描述】:

我想实现一个函数,该函数类似于二维向量(矩阵)的输入,包含其原始元素,并根据输入的列数和行数返回重新整形的版本。重构后的矩阵需要以与原来相同的行遍历顺序填充原始矩阵的所有元素。

我完成了以下代码:

vector<vector<int>> matrixReshape(vector<vector<int>>& nums, int r, int c) 
    int r_old = nums.size();
    int c_old = nums[0].size();


    if(r*c != r_old*c_old) return nums;

   vector<vector<int>> new_nums;
   new_nums.resize(r);

   // get the new matrix proper size
   for (int i =0; i< r; i++)
       new_nums[i].resize(c);
    

   int c_curr =0;
   int r_curr =0;

   // assign element
   for (int i = 0; i < r_old; i ++)
       for (int j =0; j < c_old; j++)

           if(c_curr == c)
               r_curr++;
               c_curr =0;
           
           new_nums[r_curr][c_curr] = nums[i][j];
           c_curr++;
       
   
   return new_nums;

但是,我觉得必须有更好的方法来做到这一点。我搜索了***,发现了几个问题

How can I resize a 2D C++ vector?

How can I resize a 2D vector of objects given the width and height?

但是,这些答案都“重置”了当前矩阵,这不是我想要的。所以我的问题是,有没有更快的方法通过将其元素重新排列成所需的新形状来从 2d 向量生成 2d 向量?

【问题讨论】:

您所做的不是简单的重新排列,而是创建一个新的二维数组(可能具有不同的大小)并用旧数组中的数据填充它。提示:像这样初始化new_numsvector&lt;vector&lt;int&gt;&gt; new_nums(r, vector&lt;int&gt;(c));。我没有一个好主意来避免两个for 循环,我不确定是否有更有效的方法......(当然你可以计算当前行中的空闲列数并复制一个一次一堆变量,但我希望这不会在使用优化标志时加快复制速度。) 要去你的XY。使用具有包装类的一维vector 使其看起来像vectors 中的vector 您所要做的就是将源的包装vector 复制到目标的包装vector 中(只需@ 987654332@) 并更改行和列尺寸。这很好地包装为构造函数。 【参考方案1】:

决定关注我的评论。这个答案在 X-Y 领域很远,但它应该会大大简化事情

#include <iostream>
#include <iomanip>
#include <vector>
#include <exception>

// wrapping class for 2D matrixes    
class Matrix

private:
    size_t rows, columns; // large, unsigned datatype. Don't want negative
                          // indices, so why allow them?
    std::vector<int> matrix; // 1D vector. Simple and easy to handle.
                             // also often much faster than vector of vectors 
                             // due to improved spatial locality helping 
                             // predictability of data access
public:
    // catchable exception should user request impossible dimension transformation
    class BadDimsException: public std::exception
    
    public:
        const char* what() const noexcept
        
            return "Invalid dimensions specified";
        
    ;

    // build zero-filled Matrix
    Matrix(size_t numrows, size_t numcols) :
            rows(numrows), columns(numcols), matrix(rows * columns)
    
    

    // build Matrix based on another Matrix with convertable dimensions
    // All of the heavy lifting is performed in the member initializer list
    // by simply copying data store of source Matrix
    // if matrix cannot be transformed, the thrown exception leaves user with 
    // nothing to work with and no chance of trying to continue with an un-
    // transformed matrix
    Matrix(size_t numrows, size_t numcols, const Matrix & source) :
            rows(numrows), columns(numcols), matrix(source.matrix)
    
        if (rows * columns != source.rows * source.columns)
         // Bad dimensions. Blow up.
            throw BadDimsException();
        
    

    // 2D to 1D mapping accessor
    int & operator()(size_t row, size_t column)
    
        // check bounds here
        return matrix[row * columns + column]; 
    

    // 2D to 1D mapping accessor for constant Matrix 
    int operator()(size_t row, size_t column) const
    
        // check bounds here
        return matrix[row * columns + column];
    

    // dimension accessors
    size_t getRows() const
    
        return rows;
    
    size_t getColumns() const
    
        return columns;
    
;


// stream formatter
std::ostream & operator<<(std::ostream & out, const Matrix & mat)

    for (size_t row = 0; row < mat.getRows(); ++row)
    
        for (size_t col = 0; col < mat.getColumns(); ++col)
        
            std::cout << std::setw(5) << mat(row, col);
        
        std::cout << '\n';
    
    return out;

并测试/演示用法:

int main()

    Matrix one(2, 6); // make 2x6 matrix
    int count = 0;

    // set inputs to make errors really stand out
    for (size_t row = 0; row < one.getRows(); ++row)
    
        for (size_t col = 0; col < one.getColumns(); ++col)
        
            one(row, col) = count++;
        
    

    // print initial matrix
    std::cout << one << '\n';

    // make reshaped matrix
    Matrix two(3,4, one);

    //print reshaped Matrix
    std::cout << two << '\n';
    try
    
        // make invalid reshaped matrix
        Matrix three(3, 3, one);

        // This should never print
        std::cout << three << '\n';
    
    catch (const Matrix::BadDimsException & bde)
    
        // Yay! Caught error!
        std::cout << bde.what() << '\n';
    

【讨论】:

只是一个小提示:据我了解,在您的复制构造函数中,在检查大小是否匹配之前复制数据(换句话说,复制数据后抛出异常)。在成员初始化器列表中使用辅助函数,如 rows(checked_rows(numrows, numcolumns, source.rows, source.columns)), ... 可能会在复制之前引发异常。 你是对的 @Bob__ 。我还考虑在测试和异常之后将副本移至作业,但决定最好使用最简单、最容易演示的选项。 哇!非常感谢您抽出宝贵时间。这是一个非常好的设计,阅读您的答案可以帮助我学习很多。

以上是关于在 C++ 中重塑二维向量的主要内容,如果未能解决你的问题,请参考以下文章

关于C++中的二维向量

c++中的二维向量问题

为啥我不能将整数向量推入 C++ 中的二维整数向量?

如何在 C++ 中使用构造函数初始化二维向量?

引用二维向量中的元素 (c++)

在 C++ 中使用二维向量乘以矩阵