表示多图的良好数据结构(c++)

Good data structure for representing multigraph (C++)

本文关键字:数据结构 c++ 表示      更新时间:2023-10-16

描述无方向多图(针对速度和内存进行了优化)的最佳数据结构是什么?

边列表是不合适的,因为获取顶点的邻居在我的代码中经常发生。

邻接表不是很好,因为我必须保存访问过的边的信息,当从1到3的边被访问时(假设我遍历1的邻居,找到一条通往3的边,权值为w),我必须在3的邻居列表中找到相同的边,将其标记为访问过,这很慢。

我考虑过邻接矩阵,当每个细胞都是set<Edge>时,其中Edge是一个结构,表示是否访问顶点,边的权重等信息。然而,当访问graph[0][1][i]时,如果不进行线性搜索,我就不能在graph[1][0]的边中设置相同的边。

表示多图时有什么好的方法和技巧吗?我不想要像boost::AdjacencyList这样的第三方库解决方案;我必须自己写。

编辑:很抱歉误解。这是大学的一个练习,我只能用标准库来做。图中有约束条件:0 & lt;N≤300 -顶点数0 & lt;M≤20000—边数1≤w≤500

我的内存限制为32 MB,时间限制为0.5s(我必须使用DFS遍历)。

下面是提供高效本地操作的稍微复杂的表示

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,并且对于包含它的两个列表("from"节点中的传出列表和"to"节点中的传入列表)也有prev和next指针。

使用此方法,您将获得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))为节点分配空间

使用这种方法,节点及其传出链接的所有数据将位于相邻的内存位置。