C++ 如何高效地存储非常大的图形空间,同时具有快速索引?

C++ 如何高效地存储非常大的图形空间,同时具有快速索引?,c++,data-structures,graph,C++,Data Structures,Graph,我正在处理一个包含875713个节点和5105039条边的图形。使用vector-vec(875713)或array会向我抛出一个segfault。我需要使用路径恢复计算所有对最短路径。我有哪些替代数据结构 我找到了这个,但它没有回答我的问题 编辑 我在看了这些建议后尝试了一下,似乎有效。谢谢大家帮助我 vector<vector<uint>> neighboursOf; // An edge between i and j exists if

我正在处理一个包含
875713个节点
5105039条边
的图形。使用
vector-vec(875713)
array
会向我抛出一个segfault。我需要使用路径恢复计算所有对最短路径。我有哪些替代数据结构

我找到了这个,但它没有回答我的问题

编辑

我在看了这些建议后尝试了一下,似乎有效。谢谢大家帮助我

vector<vector<uint>> neighboursOf; // An edge between i and j exists if
                                   // neighboursOf[i] contains j
neighboursOf.resize(nodeCount);

while (input.good())
{
    uint fromNodeId = 0;
    uint toNodeId = 0;

    getline(input, line);

    // Skip comments in the input file
    if (line.size() > 0 && line[0] == '#')
        continue;
    else
    {
        // Each line is of the format "<fromNodeId> [TAB] <toNodeId>"
        sscanf(line.c_str(), "%d\t%d", &fromNodeId, &toNodeId);

        // Store the edge
        neighboursOf[fromNodeId].push_back(toNodeId);
    }
}
向量邻域;//i和j之间存在一条边,如果
//[i]的邻域包含j
调整邻域的大小(nodeCount);
while(input.good())
{
uint fromNodeId=0;
uint-toNodeId=0;
getline(输入,行);
//跳过输入文件中的注释
如果(line.size()>0&&line[0]='#')
继续;
其他的
{
//每行的格式为“[TAB]”
sscanf(line.c_str(),%d\t%d“,&fromNodeId,&toNodeId);
//储存边缘
[fromNodeId]的邻居。向后推(toNodeId);
}
}

您的图形是稀疏的,也就是说,如果我们按以下方式声明一个节点,
|E |:

struct{
int node_id;
vector<int> edges; //all the edges starts from this Node.
} Node;
array<Node> nodes;
struct{
int node_id;
向量边;//所有边都从此节点开始。
}节点;
那么所有节点都可以表示为:

struct{
int node_id;
vector<int> edges; //all the edges starts from this Node.
} Node;
array<Node> nodes;
数组节点;

您可以在单个数组中存储每个节点的边列表。如果每个节点的边数是可变的,则可以使用空边终止列表。这将避免许多小列表(或类似数据结构)的空间开销。结果可能如下所示:

enum {
    MAX_NODES = 875713,
    MAX_EDGES = 5105039,
};

int nodes[MAX_NODES+1];         // contains index into array edges[].
                                // index zero is reserved as null node
                                // to terminate lists.

int edges[MAX_EDGES+MAX_NODES]; // contains null terminated lists of edges.
                                // each edge occupies a single entry in the
                                // array. each list ends with a null node.
                                // there are MAX_EDGES entries and MAX_NODES
                                // lists.

[...]

/* find edges for node */
int node, edge, edge_index;
for (edge_index=nodes[node]; edges[edge_index]; edge_index++) {
    edge = edges[edge_index];
    /* do something with edge... */
}
最小化空间开销非常重要,因为您有大量的小型数据结构。每个节点列表的开销仅为一个整数,这远小于stl向量等的开销。此外,列表在内存中连续排列,这意味着任何两个列表之间都没有浪费空间。对于可变大小的向量,情况并非如此

读取任何给定节点的所有边将非常快,因为任何节点的边都连续存储在内存中


这种数据安排的缺点是,在初始化数组和构造边列表时,需要手头有一个节点的所有边。如果按节点对边进行排序,则这不是问题,但如果边是按随机顺序排列的,则效果不佳。

尝试
vector-vec(875713)
,甚至
vector
。如果图形密集,则邻接矩阵是一种非常紧凑的表示形式。维基百科上关于图形的文章有很好的理由选择一个表示法你似乎使用了邻接矩阵。。。这将是很多:875713^2/8~90Gib@fork0这就像是用核武器来控制害虫,但却不确定它是否会杀死害虫cockroaches@user1071136实际上,您很可能已经使用了mmap:已知标准库分配器在/dev/zero上使用mmap进行内存保留。此外,人们知道在防治害虫时会反应过度,这可能是最好的办法。我会再等一些有趣的答案。这样,随机边缘访问需要O()时间。但是我希望这个数字是
你说的“随机边缘访问”是什么意思?在什么情况下你需要它?随机我指的是像
edge[src][dst]
这样的索引访问,其中src和dst不遵循特定的模式。Like edge[200][0]在edge[15][29]之后访问。我需要随机访问来从父矩阵恢复所有对最短路径。在父矩阵中,父[i][j]的值表示在i和j之间的最短路径中j之前的节点。您可以简单地使用另一个矩阵
边权重[i][j]
,它保存从
父[i][j]
j
的边的权重,该权重与
一起更新“最小化空间开销非常重要“
如何实际测量空间开销?我在Ubuntu上。我想将此方法与user1071136的方法进行比较。@Hindol查看更新的答案。总空间应在26MB左右,请参见阵列的大小。为了测量其他解决方案的开销,您可以构造一个较小的图,具有较少的边,并测量内存消耗……据我所知,当使用
-O3
编译时,GCC的
向量
,与数组相同。其他
vector
s,尤其是VisualStudio的,都是“安全的”,这意味着对它们的每一次访问都会被检查,从而降低了速度。请参见@user1071136:这里主要考虑的是空间而不是范围检查索引。每个
vector
都有固定的开销(我的计算机上有24个字节),并且容量通常大于其大小。如果有很多(比如数百万)短向量,这可能会成为一个问题。类似于此图的巨大数据结构意味着您可能受到内存带宽的限制。紧凑的表达方式会有所不同。