C++ 一个简单无向图的表示

C++ 一个简单无向图的表示,c++,data-structures,graph,discrete-mathematics,C++,Data Structures,Graph,Discrete Mathematics,我需要你的专业知识: 我将在C++中实现一个图形类,并考虑正确的表示。这些图简单且无向。顶点数量目前仅为1000个,但将来可能会更高。边缘数高达200k或更高。每个顶点都有一个颜色(int)和一个id(int)。边传输的信息不比连接到顶点传输的信息多 我存储图形,只需要访问x和y是否连接——这是我经常需要的。 初始化后,我从不删除或添加新的顶点或边(N=顶点数,M=从一开始给定的边数) 我已经可以使用的一个表示: 邻接列表展开为一个长列表。与此表示一起出现的还有一个数组,每个顶点都有起始索引。存

我需要你的专业知识:

我将在C++中实现一个图形类,并考虑正确的表示。这些图简单且无向。顶点数量目前仅为1000个,但将来可能会更高。边缘数高达200k或更高。每个顶点都有一个颜色(int)和一个id(int)。边传输的信息不比连接到顶点传输的信息多

我存储图形,只需要访问x和y是否连接——这是我经常需要的。 初始化后,我从不删除或添加新的顶点或边(N=顶点数,M=从一开始给定的边数)

我已经可以使用的一个表示:

邻接列表展开为一个长列表。与此表示一起出现的还有一个数组,每个顶点都有起始索引。存储O(2M)并检查x和y之间的边缘是否平均为O(n/m)

我想到的一个代表:

这样做的目的不是将邻接列表展开为一个数组,而是使用邻接矩阵。那么存储O(N^2)?是的,但我想将边存储在一位中,除了一个字节。(实际上是对称的两位) 示例:假设N=8,然后创建一个长度为8(64位)的向量。初始化0上的每个条目。如果顶点3和顶点5之间有一条边,则将pow(2,5)对称地添加到属于顶点3的向量条目中。因此,在顶点5的位置,顶点3的入口中有一个1,正好在3和5之间有一条边。在将我的图插入这个数据结构之后,我认为人们应该能够通过一个二进制操作在恒定时间内访问邻域:3和5是连通的吗?如果v[3]^pow(2,5)==0,则为是。当有超过8个顶点时,每个顶点需要在向量中获得多个条目,我需要执行一个模和一个除法操作来访问正确的点

您对第二种解决方案有何看法?它可能已经被知道并正在使用吗? 我认为访问O(1)是错误的吗? 如果没有真正的绩效改进,是否需要付出太多的努力

在一个大列表中加载这两个表示的原因是缓存的改进


我很高兴得到一些关于这个想法的反馈。我可能有点不对劲了——在这种情况下,请友好一点:D

如果你选择一个位矩阵,那么内存使用量是O(V^2),所以大约1Mb位或128KB,其中只有不到一半是重复的

如果你把一个边O(E)数组和另一个索引数组从顶点到第一条边,你使用200K*sizeof(int)或800KB(更大),其中一半是重复的(A-B和B-A是相同的),实际上可以保存。如果您知道(或可以将其模板化),顶点的数量可以存储在
uint16\t
中,则可以再次保存一半

要保存一半,只需检查哪个顶点的数字较低,并检查其边

要确定何时停止查找,请在下一个顶点上使用索引

所以对于你的数字来说,使用一点矩阵是好的,甚至是好的

第一个问题出现在(V^2)/8>(E*4)时,尽管边缘算法中的二进制搜索仍然比检查位慢得多。如果我们设置E=V*200(1000个顶点vs 200K条边),就会出现这种情况

这将是5120000~5MB,很容易适应现在的三级缓存。如果连通性(此处为每个顶点的平均连接数)高于200,则更好

检查边缘还将花费lg2(连通性)*K(预测失误),这将变得相当陡峭。检查位矩阵将是O(1)

除其他外,您还需要测量位矩阵何时显著破坏L3,而边列表何时仍然适合L3,以及它何时溢出到虚拟内存中


换句话说,在高连通性的情况下,位矩阵应该比连通性低得多或顶点数高得多的边缘列表更快。

如果使用位矩阵,则内存使用量为O(V^2),因此约1Mb位或128KB,其中只有略少于一半的位是重复的

如果你把一个边O(E)数组和另一个索引数组从顶点到第一条边,你使用200K*sizeof(int)或800KB(更大),其中一半是重复的(A-B和B-A是相同的),实际上可以保存。如果您知道(或可以将其模板化),顶点的数量可以存储在
uint16\t
中,则可以再次保存一半

要保存一半,只需检查哪个顶点的数字较低,并检查其边

要确定何时停止查找,请在下一个顶点上使用索引

所以对于你的数字来说,使用一点矩阵是好的,甚至是好的

第一个问题出现在(V^2)/8>(E*4)时,尽管边缘算法中的二进制搜索仍然比检查位慢得多。如果我们设置E=V*200(1000个顶点vs 200K条边),就会出现这种情况

这将是5120000~5MB,很容易适应现在的三级缓存。如果连通性(此处为每个顶点的平均连接数)高于200,则更好

检查边缘还将花费lg2(连通性)*K(预测失误),这将变得相当陡峭。检查位矩阵将是O(1)

除其他外,您还需要测量位矩阵何时显著破坏L3,而边列表何时仍然适合L3,以及它何时溢出到虚拟内存中


换句话说,在高连通性的情况下,位矩阵应优于连通性更低或顶点数更多的边缘列表。边缘列表可能更快。

具有200000条边缘的1000x1000矩阵将非常稀疏。由于图形是无向的,矩阵中的边将被写入两次:

VerticeA -> VerticeB   and   VerticeB -> VerticeA
你最终会填满矩阵的40%,剩下的将是空的

VerticeA -> VerticeB and VerticeB -> VerticeA
std::vector<std::vector<bool>> matrix(1000, std::vector<bool>(1000, false));
if( matrix[3][5] )
{
    // vertice 3 and 5 are connected
}
else
{
    // vertice 3 and 5 are not connected
}
std::vector<int> colors(1000);
1000 * sizeof(int) = 4000 B ~ 4 kB (3.9 kB)
std::unordered_map<int, int> map;
map[4] = 5;            // assign color 5 to vertice 4
std::cout << map[4];   // prints 5
1000 * 2 * sizeof(int) = 8000 B ~ 8 kB (7.81 kB)