在 cpp 中使用 2d 矢量

Posted

技术标签:

【中文标题】在 cpp 中使用 2d 矢量【英文标题】:working with 2d vector in cpp 【发布时间】:2017-04-21 10:11:40 【问题描述】:

我的问题是: 我有想要存储在数据结构中的二维数据。我事先不知道确切的行数和列数。 所以, 我正在使用二维矢量。 我知道(预期的)最大行数和列数。

    我不想浪费内存所以我可以使用resize功能吗? 或者 如果我使用 1D 矢量并将其作为 2D 访问(例如 vector[row * 宽度 + col])。如何找到矢量的宽度?

【问题讨论】:

欢迎来到 SO。目前尚不清楚您的问题是什么。是的,您可以使用resize。你试过了吗?出了什么问题? 因为您不想浪费内存,所以您预期的最大大小在这里无关紧要 是的,但我想在之后销毁未使用的空间。 释放向量的未使用内存有点棘手。 resize 不起作用,因为它只是更改了内部计数器。您需要执行vec.swap(std::vector<...>(vec)); 之类的操作,请记住它还会复制所有项目 【参考方案1】:

关于您在第一个选项中尝试访问的方式,它无法完成。您需要在那里已经有一个值,然后才能分配给它。下面的代码展示了一个人为但实用的解决方案和演示。

#include <vector>
#include <iostream>

using namespace std;

void add_element(vector<vector<int>> &data, int row, int col, int val)

    while (data.size() <= row) data.push_back(vector<int>());
    while (data[row].size() <= col) data[row].push_back(int());
    data[row][col] = val;


int main()

    vector<vector<int>> data;
    int col_idx = 0;
    cout << "Before...\n";
    cout << data.size() << " rows.\n\n";
    for (auto &row : data)
        cout << "row " << col_idx++ << ": " << row.size() << " columns.\n";
    add_element(data, 4, 5, 21);
    cout << "After...\n";
    col_idx = 0;
    cout << data.size() << " rows.\n\n";
    for (auto &row : data)
        cout << "row " << col_idx++ << ": " << row.size() << " columns.\n";
    cout << "\nAccess...\n";
    cout << "data[4][5] == " << data[4][5] << endl;
    data[4][5] = 99;
    data[2].push_back(33);
    data[2].push_back(41);
    data[2].push_back(55);
    cout << "data[2][2] == " << data[2][2] << endl;
    data[2][2] *= 2;
    cout << "data[2][2] == " << data[2][2] << endl;
    cout << "data[4][5] == " << data[4][5] << endl;

    return 0;

【讨论】:

您可以通过使用 .resize() 进行批量分配来提高效率。【参考方案2】:

使用 std::vector::reserve()resize() 函数来保留各个行。

std::vector< std::vector<int> > data;
data.resize(nMaxRows);
for(int i=0;i<nMaxRows;i++)
    data[i].reserve(nMaxColumn);

【讨论】:

这种场景下如何访问元素? 这并不能解决问题#1——无论如何,所有内存都将被预先分配。 在这种情况下如何访问元素(存储和检索)? 数据[i][j] 不工作。它正在生成:向量下标超出范围异常。 它按预期工作,问题是您正在尝试访问不存在的元素。检查你的索引。【参考方案3】:

至于选项 2,如果您使用表示二维的一维向量,请使用包装器,例如

class Vector2D

    private:

        vector<int> m_data;
        unsigned int m_width;
        unsigned int m_height;

        unsigned int index_transformation((const unsigned int i, const unsigned int j) const;

    public:

        Vector2D(const int default_value = 0);
        Vector2D(const unsigned int width, const unsigned int height, const int default_value = 0);

        void resize(const unsigned int width, const unsigned int height);

        int& operator() (const unsigned int i, const unsigned int j);
        const int& operator() (const unsigned int i, const unsigned int j) const;

        const unsigned int& width() const;
        const unsigned int& height() const;

(在我的示例中首先进行私有化,以便您轻松理解结构)

重点是,宽度优先或高度优先几乎是任意的,如何做到这一点的责任应该放在一个地方,比如在包装器中。在这种情况下,它甚至被简化为一个方法,index_transformation。

edit:Andy T 问我为什么喜欢这种结构,事实是,我不喜欢。基本上,我是说如果你这样做,那么......但你可以轻松地使用vector&lt;vector&lt;int&gt; &gt;,而不是在那里,没有问题。也就是说,您可能还想包装一个二维向量,原因很简单,这可以确保向量保持规则(例如,与 1,2,3 不同,它的子向量长度为2 和长度 1) 之一 - 也就是说,如果向量被大量传递。一个简单的二维向量在小范围内或在任何可以轻松确保规律性的上下文中都是完全可以的。

也就是说,对于您的一般问题:初始化很可能不会受到伤害。也就是说,您可以简单地使用预期数量的元素(或者,更好地说,它的上边界)初始化您的向量。这当然会导致 O(n) 的工作量,但很可能,与之后的工作量相比,这个工作量会很小。如果不是,因为您的预期可能远远大于实际使用量,或者如果在后面的算法中只完成了一些动作,是的,保留,这里没有问题。缺点是你仍然需要调整你可以忘记的向量的大小,但这很容易检测到。

【讨论】:

为什么你认为平面向量比vector&lt;vector&lt;T&gt;&gt;更好? @AndyT 好问题。其实我没有,我或多或少地说如果他选择了选项2,那么他应该那样做。但是,是的,要进行编辑。【参考方案4】:

在 cmets 进行讨论之后,问题似乎是访问 2D 矢量项目时它们尚不存在。

要设置具有任意索引的项目:

template <typename T>
void set(std::vector<std::vector<T>>& vec, size_t i, size_t j, T const& val)

    if (vec.size() <= i)
        vec.resize(i + 1);
    auto&& row = vec[i];
    if (row.size() <= j)
        row.resize(j + 1);
    row[j] = val;

要获得一个项目,你需要决定你想要什么行为:当项目不存在时抛出一个异常,或者用一些默认值创建它并返回。

您询问了有关释放未使用内存的问题。您只能在向量末尾释放内存,因为它的所有项目一个接一个地存储在一个内存块中。如果您的数据结构稀疏(有很多空白)vector 不是最佳选择。最简单的解决方案是通过map 模拟稀疏容器,例如std::map&lt;size_t, std::map&lt;size_t, T&gt;&gt;。在这种情况下,当你这样做时

map[i][j] = val;

即使 (i, j) 位置的项目在按需创建之前不存在。要释放未使用的内存,您需要通过 std::map&lt;Key, Value&gt;::erase(key) (http://www.cplusplus.com/reference/map/map/erase/) 从地图中删除项目

【讨论】:

【参考方案5】:

您可以使用矩阵:

#ifndef MATRIX_H
#define MATRIX_H

#include <vector>
#include <iostream>
#include <initializer_list>
#include <stdexcept>
#include <sstream>

using namespace std;

template<typename T>
class Matrix 

public:
    typedef T value_type;
    typedef vector<T> row_t;
    // ctor
    Matrix()  
    Matrix(int rows, int cols)
        for (int i = 0; i<rows; ++i)
            mat.push_back(row_t(cols));
        
    
    Matrix(int rows, int cols, const initializer_list<T>& l)
        for (int i = 0; i<rows; ++i)
            mat.push_back(row_t(cols));
        
        auto it = l.begin();
        int y=0, x=0;
        while (it != l.end())
            mat[y][x++] = *it++;
            if (x == cols)
                ++y;
                x = 0;
            
        
    
    Matrix(const Matrix<T>& m)
        for (auto e : m.mat)
            mat.push_back(e);
        
    
    // method
    int getRowCount()
        return mat.size();
    

    int getColumnCount()
        if (mat.size() == 0)
            return 0;
        return mat[0].size();
    

    row_t& operator[](int n)
        if ( n<0 || static_cast<size_t>(n) >= mat.size() )
            ostringstream oss;
            oss << "bad index " << n << " out of range [0.." << mat.size() << "]";
            throw runtime_error(oss.str());
        
        return mat[n];
    

    Matrix<T>& operator=(const Matrix<T>& m)
        if (&m != this)
            mat.clear();
            for (auto e : m.mat)
                mat.push_back(e);
            
        
        return *this;
    

private:
    vector<row_t> mat;

;

#endif // MATRIX_H

【讨论】:

以上是关于在 cpp 中使用 2d 矢量的主要内容,如果未能解决你的问题,请参考以下文章

使用纵横比旋转2D矢量

使用纵横比旋转 2D 矢量

在访问该类的函数时,如何在我的类的 2D 矢量上使用点运算符?

如何删除 2D 矢量网格中的部分路径?

无法使用 2d 矢量成员引用对象

c_cpp 使用矢量擦除实现队列