【问题标题】:How to iterate over a 2d vector using a 1d index如何使用一维索引迭代二维向量
【发布时间】:2020-11-27 19:13:49
【问题描述】:

给定一个 int 向量的向量(例如),以下代码允许我将平面位置映射到二维索引并访问该元素:

int x = 0, y = 0, acc = 0;

while ((acc = myVector[x].size()+acc) <= position) {
     x++;
     y= acc;
}
y = position - y;

我想知道是否有一种方法可以使用单个指针遍历我的二维向量的所有元素,从而将其增加位置编号。

编辑1:

std::vector<std::vector<int>> myVector;

myVector.emplace_back();

for (int i = 0; i < 10; i++) {
    myVector[0].emplace_back(0);
}

myVector.emplace_back();
for (int i = 0; i < 5; i++) {
    myVector[0].emplace_back(1);
}

myVector.emplace_back();
for (int i = 0; i < 8; i++) {
    myVector[0].emplace_back(2);
}

for (auto row : myVector) {
    for (auto col : row) {
        std::cout << col << std::endl;
    }
}

我需要创建一个指向myVector[0][0] 指向的第一个元素的指针,然后在第一行的末尾和下一行的开头之间创建一个链接,以便我可以使用单个指针对其进行迭代。 如果我需要位置 12 的元素,那么只需增加指针,我就可以获得位置 (1,1) 的元素。 基本上我正在尝试使用 std::vectors 来模拟 c 数组的行为

【问题讨论】:

  • 请创建一个minimal reproducible example 并展示一些代码应该做什么的示例。
  • 你需要什么?如果将其用作 1D 平面向量,为什么会有 2D 向量?一周(或一小时后)你和其他人都不会理解这段代码......
  • 这在 C 风格的数组中是可能的,因为它们直接彼此相邻地存储在内存中。通过使用类,例如std::vector,会存储其他成员变量,例如长度,因此在主向量的每个子向量之间,会有你不想迭代的东西。
  • 安东尼奥,布莱尔回答了你很好的解决方案。您对指针的渴望有一个问题:向量可以在修改后更改内存块(您不应该使用固定指针)。还有那些你应该存储在某个地方的指针链接。
  • “我正在尝试模拟 c 数组的行为” -- 不过,行为上有一个关键区别。您的向量不能保证具有相同的长度,而 2D 数组在次要维度中必须具有统一的长度。对于更复杂的情况,您应该期待更复杂的解决方案。

标签: c++ algorithm pointers vector iterator


【解决方案1】:

我想知道是否有一种方法可以使用单个指针遍历我的二维向量的所有元素

没有,没有。你不能假设第一个向量的元素后面跟着第二个向量的元素,等等。

您可以改为使用std::vector&lt;int&gt; 来连续存储所有元素,然后将std::vector&lt;std::span&lt;int&gt;&gt; 构造为这些元素的“2d”视图

std::vector<int> raw;

for (int i = 0; i < 10; i++) {
    raw.emplace_back(0);
}

for (int i = 0; i < 5; i++) {
    raw.emplace_back(1);
}

for (int i = 0; i < 8; i++) {
    raw.emplace_back(2);
}

std::vector<std::span<int>> myVector;
myVector.emplace_back(raw.data(), 10);
myVector.emplace_back(raw.data() + 10, 5);
myVector.emplace_back(raw.data() + 15, 8);

for (auto row : myVector) {
    for (auto col : row) {
        std::cout << col << std::endl;
    }
}

See it live

【讨论】:

  • 如果 std::vector 存储元素 contiguously 并包含 std::vectors,为什么不能保证所有元素都是连续的并且位于第一个 std::vector 的末尾( row) 开始第二个包含的?
  • @AntonioSantoro 因为没有保证第二个向量的元素紧跟第一个向量的元素存储。您可以考虑(实际实现可能完全不同)向量作为一个类,它存储指向元素的指针以及大小和容量。在向量的向量中,这些是连续排列的数据。
  • @AntonioSantoro 因为连续的东西是向量的 {pointer-to-data, size, capacity} (或等效的)数据成员。每个向量都为其元素分配了一些动态存储空间。通常,向量的向量不能协调内部向量分配是连续的,因为您可能会在中间添加元素。当您将元素添加到另一个向量时,该标准不允许使 some other 向量的迭代器无效。
  • 现在很清楚了,谢谢。有没有动态大小的 stl 容器来满足我的需要?
  • @AntonioSantoro all 类型具有静态大小。如果您想要连续的元素,请使用一维向量
【解决方案2】:

编写您自己的容器类,执行以下操作:

  1. 将向量向量作为构造函数参数。将向量地址的向量存储为其成员。
  2. 实现 get(size_t p) 以迭代如下:
size_t i;
for (i = 0; i < v.size() && p > v[i].size(); i++)
{
   p -= v[i].size();
}
return v[i][p];
  1. 重载[]指向获取

【讨论】:

  • 如果使用“矩形”,您的意思是每行需要相同的长度,那么我的情况不是这样
  • 编辑为允许非矩形,但以效率/速度为代价。
  • 您提出的get() 与问题中的功能基本相同。不是添加尺寸,然后从位置中减去总和,而是从位置中减去每个尺寸。我发现您的版本更易于阅读,但这仍然是问题试图取代的方法。
  • 是的,它已经退化为...如果必须适应非矩形形状和未来的编辑,我认为没有更好的解决方案。请记住,2D C 数组(在原始问题中提到过)将始终为 1:矩形,2. 分配后大小不变。
【解决方案3】:

在 c++20 中,使用 &lt;ranges&gt; 标头直接支持此功能。所以你可以简单地这样做:

namespace srv = std::ranges::views;

for (auto i : myVector | srv::join)
     std::cout << i << std::endl;

这是demo

使用 range-v3,您可以像这样创建连接视图的vector

namespace rv = ranges::views;
    
auto j = myVector | rv::join | ranges::to<std::vector<int>>;

然后索引到任意位置。

这是demo

【讨论】:

  • 我需要一个指针,允许我通过链接每个行向量的末尾来随机访问 myVector
  • 如果使用range-v3,加入后可以使用ranges::to&lt;&gt;,在加入的容器上可以使用随机访问。
  • @AntonioSantoro 你绝对需要随机访问吗?还是std::next 就足够了?您需要遍历外部向量,计算内部向量的大小,以了解哪个向量包含您要查找的元素。
  • @AntonioSantoro 添加了随机访问的演示。但这会创建一个新向量,这可能不是您想要的。
猜你喜欢
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-26
相关资源
最近更新 更多