描述无定向多图(针对速度和内存进行了优化)的最佳数据结构是什么?
边缘列表是不合适的,因为在我的代码中经常出现一个顶点的邻居。
邻接列表并不好,因为我必须保留有关被访问边的信息,并且当访问1到3的边时(比如我遍历1的邻居并找到导致3的边缘并具有权重w
),我必须在3的邻居列表中找到相同的边缘,将其标记为已访问,这很慢。
我已经考虑了每个单元格为set<Edge>
时的邻接矩阵,其中Edge
是一个结构,表示是否访问了顶点,边缘权重等信息。但是,当{ {1}}如果没有线性搜索,我无法在graph[0][1][i]
的边缘设置相同的边缘。
表示多图时是否有任何好的方法和技巧?我不想要第三个库解决方案,如graph[1][0]
;我必须自己写。
编辑:对不起有误。这是大学的练习,我只能使用标准库来完成它。该图有约束: 0&lt; n≤300 - 顶点数 0&lt; m≤20000 - 边数 1≤w≤500
我的内存限制为32 MB,时间限制为0.5秒(我必须使用DFS遍历)。
答案 0 :(得分:3)
然而,有些复杂的表示提供了有效的本地操作,如下所示
struct Link;
struct Node
{
Link *firstIn, *lastIn, *firstOut, *lastOut;
... node data ...
};
struct Link
{
Node *from, *to;
Link *prevInFrom, *nextInFrom, *prevInTo, *nextInTo;
... link data ...
};
基本上每个Node
都有两个双链表,一个用于传入链接,另一个用于传出链接。每个Link
知道开始和结束Node
,并且还包含包含它的两个列表的prev和next指针(“from”节点中的传出列表和“to”中的传入列表节点)。
使用此方法,您将获得O(1)
节点和链接创建和销毁,O(inbound_deg(node))
用于查找哪些链接到达节点,O(outbound_deg(node))
用于查找哪些链接正在离开节点。该结构还支持同一对节点之间的多个连接以及多个循环。
所需的空间是每个节点和每个链路的固定数量,但开销可以是好的或不是取决于应用程序(每个节点4个指针和每个链接6个指针)。
使用简单列表而不是双链表,开销变为每个节点2个指针,每个链接4个,但链接删除变为O(outbound_deg(from) + inbound_deg(to))
并且不再是常量。
另请注意,所示结构不是高速缓存友好的,并且在现代台式计算机中可能是更“强力”的方法,例如,指针的向量而不是双向链表可以提供更好的速度,具体取决于列表的大小以及您改变图结构的频率。
甚至可以将链接对象拆分为嵌入前向链接数据,例如在“from”节点中,将后向指针保留在“to”节点中。
struct Node
{
struct OutgoingLink
{
Node *to;
int incoming_index;
... link data ...
};
struct IncomingLink
{
Node *from;
int outgoing_index;
};
std::vector<OutgoingLink> outgoing_links;
std::vector<IncomingLink> incoming_links;
... node data ...
};
如果您大部分时间都在前进方向上遍历链接,并且链接没有添加到现有节点,那么更好的方法是只为节点和输出链接数据使用一个内存块,但不幸的是C ++不容易支持。
在C中它将是
typedef struct TOutgoingLink
{
struct TNode *to;
int incoming_index;
... link data ...
} OutgoingLink;
typedef struct TIncomingLink
{
struct TNode *from;
int outgoing_index;
} IncomingLink;
typedef struct TNode
{
... node data ...
int num_incoming_links;
int num_outgoing_links;
IncomingLink *incoming_links; // separate array
OutgoingLink outgoing_links[1]; // embedded array starting here
} Node;
使用malloc(sizeof(Node) + (num_outgoing_links-1)*sizeof(OutgoingLink))
为节点分配空间。
使用这种方法,节点及其输出链路的所有数据都将位于相邻的存储器位置。