这是一个名为Graph
的类的构造函数。在构造函数中,我试图初始化某些东西,这里是有效的构造函数,即它运行并完成:
Graph(const float density, const int numVertex = 50): numEdges(0) {
graph.resize(numVertex, vector<Vertex>(numVertex));
for (int s = 0; s < numVertex; ++s) {
for (int k = 0; k < s; ++k )
if (edge_exist(density)) {
graph[s][k].visited = graph[k][s].visited = false;
graph[s][k].distance = graph[k][s].distance = _MAX;
++numEdges;
int distance = rand() % 10 + 1;
graph[s][k].edges.emplace_back(piiv(distance, graph[k][s]));
graph[k][s].edges.emplace_back(piiv(distance, graph[s][k]));
}
}
}
typedef pair<int, Vertex> piiv;
vector<vector<Vertex> > graph;
该类有一个名为graph的向量对象,它是一个名为Vertex的结构向量的向量。
typedef struct vert {
std::list<std::pair<int, vert> > edges;
int distance;
bool visited;
} Vertex;
现在,如果我按原样离开顶点结构,而是使向量只是一维向量
vector<Vertex> graph;
并将构造函数更改为:
Graph(const float density, const int numVertex = 50): numEdges(0) {
graph.resize(numVertex);
for (int s = 0; s < numVertex; ++s) {
for (int k = 0; k < s; ++k )
if (edge_exist(density)) {
graph[s].visited = graph[k].visited = false;
graph[s].distance = graph[k].distance = _MAX;
++numEdges;
int distance = rand() % 10 + 1;
graph[s].edges.emplace_back(piiv(distance, graph[k]));
graph[k].edges.emplace_back(piiv(distance, graph[s]));
}
}
}
现在,此更改导致代码运行的时间远远超过向量为2维时的代码。我没有耐心去弄清楚它运行了多长时间,但我知道它比第一个运行速度慢得多,并且没有明显的原因。对我来说不是很明显,但我敢打赌,这里的某些人对于为什么会有这样的见解。
所以我的问题是,是什么导致程序中这个看似无穷无尽的循环? 如果它有帮助,这就是构造函数的调用方式:
Graph G(0.4);
我已经跟踪了第二个构造函数实现中最后两行的问题:
graph[s].edges.emplace_back(piiv(distance, graph[k]));
graph[k].edges.emplace_back(piiv(distance, graph[s]));
所以我想真正的问题是上面的内容与第一个构造函数中的内容有何不同?
宾果! 在我调试时,我决定将Vertex结构更改为如此声明:
typedef struct vert {
std::list<std::pair<int, vert&> > edges;
int distance;
bool visited;
} Vertex;
这似乎解决了这个问题,但为什么呢?为什么传递Vertex对象的值与通过引用传递相比不起作用?
答案 0 :(得分:0)
@Joe Z很好地总结了发生这种情况的原因。
std::list<std::pair<int, vert> > edges;
通过将Vertex
边声明为包含其他Vertex
(按值)的其他边的列表,这意味着每次将新顶点添加到列表中时,复制构造函数立即生效,并开始复制vert
的所有边缘,这也意味着复制该Vertex
的所有子节点,依此类推,以此类推... < / p>
如您所见,这种无法满足的复制意味着随着图形的密集,复制将花费更多的时间,如果我们决定在图形中引入一个循环,上帝会帮助我们,因为这意味着我们将在内存耗尽之前该程序完成。
更改edges
属性以具有以下类型:
std::list<std::pair<int, vert&> > edges;
现在,这意味着每次我们向图形添加新边时,我们只需维护对其他顶点的引用即可,而不是复制它们。这也意味着我们实际上可以检测到何时从另一个顶点移除了一条边。
此外,我实际上将这条边定义为:
std::list<std::pair<int, const vert&> > edges;
这只是一个好习惯,因为我们要确保边是不可变的,并且引用另一个顶点并不意味着能够更改其属性。