边缘的列表将是不合适的,因为在我的代码中经常发生顶点的邻居.
邻接列表不好,因为我必须保留关于访问边缘的信息,并且当访问1到3的边缘时(说我正在遍历1的邻居,并发现一个边缘导致三个并且具有权重w)我必须在3的邻居列表中找到相同的边缘来标记为被访问的,这是缓慢的.
我想到了每个单元格被设置为< Edge>的邻接矩阵其中Edge是表示如果访问顶点的信息的结构,边缘的权重等.然而,当访问图[0] [1] [i]时,我不能在图[1]中设置相同的边,[0]的边缘没有线性搜索.
在代表多重图形时,有什么好的方法和技巧吗?我不想要第三个图书馆解决方案,如boost :: AdjacencyList;我必须自己写.
编辑:对不起,误会了.这是大学的一个练习,我只能使用标准库来做.该图有约束:
0 < n≤300 - 顶点数
0 < m≤20000 - 边数
1≤w≤500
我的内存限制为32 MB,时间限制为0.5秒(我必须使用DFS进行访问).
解决方法
struct Link; struct Node { Link *firstIn,*lastIn,*firstOut,*lastOut; ... node data ... }; struct Link { Node *from,*to; Link *prevInFrom,*nextInFrom,*prevInTo,*nextInTo; ... link data ... };
基本上每个节点有两个双链表,一个用于传入链路,一个用于传出链接.每个链接知道起始和结束节点,并且还包含包含它的两个列表的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)),并且不再是常量.
还要注意,所示的结构不是缓存友好的,并且在现代台式计算机中可能是更加“强力”的方法,指针的代码而不是双向链表可以提供更好的速度,具体取决于列表的大小以及图形结构的变化频率.
拆分链接对象以将例如“从”节点中的前向链接数据嵌入到“从”节点中,将后向指针保留在“到”节点中甚至可能是有意义的.
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))来分配节点的空间.