用于表示多图(C ++)的良好数据结构

时间:2012-12-24 14:43:30

标签: c++ data-structures graph

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

边缘列表是不合适的,因为在我的代码中经常出现一个顶点的邻居。

邻接列表并不好,因为我必须保留有关被访问边的信息,并且当访问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遍历)。

1 个答案:

答案 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))为节点分配空间。

使用这种方法,节点及其输出链路的所有数据都将位于相邻的存储器位置。