尝试使用向量数组实现图形
Posted
技术标签:
【中文标题】尝试使用向量数组实现图形【英文标题】:Trying to implement graph using array of vectors 【发布时间】:2015-11-12 09:41:08 【问题描述】:我正在尝试使用向量数组来实现图形。我在这样做时遇到了难以置信的困难。向量将保存与顶点 v 相邻的顶点,这是名为 uvertices 的数组的索引。
这是我目前所拥有的。
class Graph
public:
int V; //this will represent the number of vertices in the graph
std::vector<int>* vertices;
Graph(int V)
vertices = new std::vector<int>[V];
在这里,我创建(或认为并打算创建)一个包含向量的大小为 V(V 是图中的顶点数)的 ARRAY。然后我希望能够创建边缘,所以我有
void Graph::insert_edge(int v, int u)
作为一种方法。我想将值 u 推回保存顶点 v 的邻接列表的向量中。我的向量数组的每个索引(名为 vertices)代表图中每个顶点 v 的标识符。所以我想做类似的事情
vertices[v].push_back(u);
我惊讶地发现,当我输入 vertices[v]. 时,Intellisense 给了我一个可能的函数列表,包括 push_back。我感到惊讶的原因是因为我实际上并没有创建任何向量,但我就这样离开了。这显然是行不通的,所以我开始调试,我意识到每当我第一次调用 insert_edge 时,它都会告诉我数组“顶点”的大小和容量为 0。尽管我将图形数据结构创建为
Graph G(8);
我花了大约两个小时试图弄清楚如何使用它,但我没有成功。
如何制作,以便在插入边时,我可以查看顶点数组中的正确索引 v,并访问该数组索引保存的向量的 push_back 函数,以便我可以添加顶点 u 到顶点 v 的邻接表。
另外,我真的真心恳求,如果人们给我投反对票或投票结束我的话题,请告诉我原因。花时间把所有这些都写出来并确保写得很清楚只是为了被否决而没有人告诉我他们为什么要对我投反对票,这真是令人沮丧。
【问题讨论】:
您确定您的意思是new std::vector<int>[V]
而不是new std::vector<int>(V)
?
@CompuChip 是的。我正在动态创建一个包含 std::vector我惊讶地发现,当我输入 vertices[v]. 时,Intellisense 给了我一个可能的函数列表,包括 push_back。我感到惊讶的原因是因为我实际上并没有创建任何向量。
这不足为奇。动态数组是在运行时分配的,而智能感知和所有其他静态分析器不运行代码,因此它们永远无法判断变量在运行时的状态。 Intellisense 确实知道 vertices[v]
的类型是 std::vector<int>
并且它知道该类型具有哪些成员函数。
但您也不应该感到惊讶,因为您确实创建了向量。在Graph
的构造函数中。
我意识到每当我第一次调用 insert_edge 时,它都会告诉我数组“顶点”的大小和容量为 0。
这里有误会。 vertices
是一个指针。在构造函数运行之后,它指向一个内存块,该内存块是一个动态数组,恰好是 std::vector<int>
的 V
实例。 vertices
没有 size
或 capacity
这样的成员。然而,在构造之后,该动态数组中的每个std::vector<int>
都是空的。直到你推入顶点。
这显然行不通。
您的insert_edge
似乎工作正常。但是你确实有泄漏内存的问题(除非你有一个没有显示的正确析构函数)和不正确的复制构造函数和复制赋值运算符的问题(不遵循三规则......除非你已经完成那但没有显示出来)。您可以通过使用向量向量而不是手动管理内存来解决这两个问题。
如何制作,以便在插入边时,我可以查看顶点数组中的正确索引 v,并访问该数组索引保存的向量的 push_back 函数,以便我可以添加顶点 u 到顶点 v 的邻接表。
你已经想通了:
vertices[v].push_back(u);
同样的语法也适用于向量的向量。
您也永远不会设置Graph::V
,但如果您使用向量的向量,则首先不再需要它,因为可以使用vertices.size()
访问它。
【讨论】:
我的问题是我试图打印邻接列表但它没有打印,这特别是因为我没有设置 V。这可以让我省去几个小时的头痛。 尝试调试并查看...当顶点只是一个指针时,实际数组非常混乱 @FrostyStraw 又一个使用向量的理由,嗯? :) 如果我被要求在面试中实现任何图形算法,我肯定会使用向量向量【参考方案2】:我认为您应该仔细考虑您对数据结构的选择。在图/算法社区中,图的两种标准表示是邻接表或邻接矩阵。参见例如 Standard graph representations
请说明您使用 std::vector 的原因,并重新表述问题。链表(如在邻接表表示中)比向量表示更有效地删除边缘。邻接矩阵也是如此。
【讨论】:
我以前使用过链表。但是,当我尝试使用原始图(使用 std::lists 数组)实现广度优先搜索时遇到了一个问题,当您查看特定顶点并想要查看其邻接列表时(到将它添加到队列中),我无法遍历列表以将每个顶点添加到队列中。如果每个顶点是存储顶点的向量而不是列表,那么访问每个顶点似乎更容易。 对于广度优先搜索,我认为首选的 repr 是邻接列表 (O(|V|+|E|)),对于同样的问题,矩阵是 O('V'^2)。所以也许你应该回到使用邻接列表,如果你的问题没有我不知道的任何特殊并发症,它们应该可以很好地工作。 @ErikAlapää 从链表中删除边并不比从向量中删除边更有效,因为邻接列表中边的顺序无关紧要。 FrostyStraw 确实使用了邻接表表示,尽管他们选择使用向量而不是链表来实现列表。对于大多数用例,我会说向量作为邻接列表更有效。 @user2079303:正如你所说,这取决于使用情况。如果您处理与顶点相邻的所有顶点,则向量会更快。但很明显,一般来说,删除例如一个顶点在链表中比在向量中更快。在 std::vector 中,erase 在被移除元素之后的元素数量是线性的。列表适合在中间擦除,向量则不行。 @ErikAlapää 如果元素的顺序不重要 - 因为在邻接列表的情况下不重要 - 然后从向量中删除 - 从任何索引 - 是一个常数时间操作:如果删除index 不是最后一个,则将最后一个元素移动到 index,然后pop_back
。如果删除的索引是最后一个,那么只需pop_back
。只有在必须保持顺序或元素非常大但不能有效移动时,链表才能更有效地删除。以上是关于尝试使用向量数组实现图形的主要内容,如果未能解决你的问题,请参考以下文章