使用邻接表创建图

5
我正在学习Skiena的《算法设计手册v3》。
我看到有些问题提到书中有一些拼写错误,但我不确定这是不是其中之一,或者只是因为我理解有误。
请考虑以下内容:
typedef struct edgenode {
    int y;                   /* adjacency info */
    int weight;              /* edge weight, if any */
    struct edgenode *next;   /* next edge in list */
} edgenode;

typedef struct {
    edgenode *edges[MAXV+1];  /* adjacency info */
    int degree[MAXV+1];       /* outdegree of each vertex */
    int nvertices;            /* number of vertices in the graph */
    int nedges;               /* number of edges in the graph */
    int directed;             /* is the graph directed? */
} graph;

void insert_edge(graph *g, int x, int y, bool directed) {
    edgenode *p;        /* temporary pointer */

    p = malloc(sizeof(edgenode));    /* allocate edgenode storage */

    p->weight = 0;
    p->y = y;
    p->next = g->edges[x];

    g->edges[x] = p;    /* insert at head of list */

    g->degree[x]++;

    if (!directed) {
        insert_edge(g, y, x, true);
    } else {
        g->nedges++;
    }
}

据我所了解,void insert_edge(graph *g, int x, int y, bool directed)函数通过将节点xy添加到edges数组中,连接了数组索引处的两个节点。
下面这段代码让我感到困惑:
p->y = y;
p->next = g->edges[x];

g->edges[x] = p;    /* insert at head of list */

这个是怎么工作的?假设我的输入是 x = 3, y = 4,而且这是第一个输入。
对于有向图,我期望得到类似于 3 -> 4 的结果。
  1. p->y = y; 很好理解,x 的邻接点是 y
  2. p->next = g->edges[x]; 但是 edges 数组被初始化为 null。这样会导致 3.next == NULL 而不是 3.next == 4,对吗?
  3. g->edges[x] = p; 什么?edges[x] == x Node,这让我感到困惑。
完整的代码可以在 graph.cgraph.h 找到。
我觉得

p.next = y;y.next = NULL现在是这样的,也就是第一次插入。也许是因为我对C语言有点生疏,需要一些帮助。


1
edges 是一个链表。那部分只是将其添加到该链表的前面。.next 实际上是边缘链表中的下一条边。(另外,我认为 x 以某种方式被隐含了。也许这个链表稍后会转换成数组,并且 x 是从数组索引中隐含的?) - Ouroborus
1
是的,在“transpose”函数中可以看到,通过查看边在链表中的深度,可以推断出变量x的含义。 - Ouroborus
这有点令人困惑,我明白节点有下一个节点的类比,就像链表一样,但是边有下一条边?听起来很混乱。我会继续阅读以便更好地理解。 - VIAGC
这实际上只是"事物"和"下一件事"。这部分与图中的节点/边无关,严格来说,它只用于管理数据存储。 - Ouroborus
1
只是对上面的评论进行一个更正:edges 不是一个链表,而是一个(固定大小的)数组。在更深层次上有链表:edges[0]edges[1] 等都是链表(即指向这些链表第一个节点的指针),新的 edgenode 节点会被添加到这些链表的开头,也就是说头部始终是最新添加的节点,因此每次插入都需要更新 edges[x] - trincot
显示剩余2条评论
1个回答

2
据我所了解,insert_edge函数通过将节点x和y添加到edges数组来连接两个节点。
我不会称之为“将其添加到edges数组中”,因为edges数组的长度是固定的,并且每个节点(而不是每个)都有一个条目。 edges数组的索引标识了一条边的“起始”顶点,而edgenodey字段标识了相应的“终点”顶点。
对于每个顶点x,这段代码维护着一条边的链表(不是edges,而是edges[x]),初始时为空。
让我们以一个示例图进行说明:

enter image description here

...在初始化之后,将其边填充如下:

insert_edge(g, 0, 1, true); 
insert_edge(g, 0, 2, true); 
insert_edge(g, 1, 2, true); 
insert_edge(g, 2, 3, true); 
insert_edge(g, 3, 4, true); 

让我们看看这个如何填充图数据结构。我们从这个状态开始 g (在这个表示中,我假设 MAXV 为4):
    ┌───────────┬────┬────┬────┬────┬────┐    
g ─►│ edges     │  - │  - │  - │  - │  - │
    ├───────────┼────┼────┼────┼────┼────┤
    │ degree    │  0 │  0 │  0 │  0 │  0 │
    ├───────────┼────┼────┴────┴────┴────┘
    │ nvertices │  5 │ 
    ├───────────┼────┤
    │ nedges    │  0 │
    ├───────────┼────┤
    │ directed  │true│
    └───────────┴────┘

连字符代表nullpointer值。我假设所有这些都已经正确初始化。
然后insert_edge(g, 0, 1, true)将创建一个p节点,该节点被分配和初始化为以下内容:
    ┌────────┬────┐
p ─►│ weight │  0 │
    ├────────┼────┤
    │ y      │  1 │
    ├────────┼────┤
    │ next   │  - │
    └────────┴────┘

值得注意的是,next字段为nullptr,因为在g->edges[0]处找到了该值。
然后,下一个语句g->edges[x] = p建立了链接,在两个计数器递增之后,函数完成了它的工作。
                      ┌────────┬────┐
                  p ─►│ weight │  0 │
                      ├────────┼────┤
                      │ y      │  1 │
                      ├────────┼────┤
                   ┌─►│ next   │  - │
                   │  └────────┴────┘
                   │
                   │
                   │
                   │
    ┌───────────┬──│─┬────┬────┬────┬────┐    
g ─►│ edges     │  ┴ │  - │  - │  - │  - │
    ├───────────┼────┼────┼────┼────┼────┤
    │ degree    │  1 │  0 │  0 │  0 │  0 │
    ├───────────┼────┼────┴────┴────┴────┘
    │ nvertices │  5 │ 
    ├───────────┼────┤
    │ nedges    │  1 │
    ├───────────┼────┤
    │ directed  │true│
    └───────────┴────┘

现在我们来看更有趣的一个:insert_edge(g, 0, 2, true);。再次,p 指向一个新的 edgenode,但这次 g->edges[0] 不是 nullptr,所以 p->next 将指向先前添加的 edgenode。在 insert_edge 完成后,我们得到以下结果:
                      ┌────────┬────┐  ┌────────┬────┐
                  p ─►│ weight │  0 │  │ weight │  0 │
                      ├────────┼────┤  ├────────┼────┤
                      │ y      │  2 │  │ y      │  1 │
                      ├────────┼────┤  ├────────┼────┤
                   ┌─►│ next   │  ────►│ next   │  - │
                   │  └────────┴────┘  └────────┴────┘
                   │
                   │
                   │
                   │
    ┌───────────┬──│─┬────┬────┬────┬────┐    
g ─►│ edges     │  ┴ │  - │  - │  - │  - │
    ├───────────┼────┼────┼────┼────┼────┤
    │ degree    │  2 │  0 │  0 │  0 │  0 │
    ├───────────┼────┼────┴────┴────┴────┘
    │ nvertices │  5 │ 
    ├───────────┼────┤
    │ nedges    │  2 │
    ├───────────┼────┤
    │ directed  │true│
    └───────────┴────┘

然后对于其他边继续进行同样的操作:每次新的edgenode被添加到一个以edges数组中存储的头指针为首的链表之前。我们有5个链表:每个顶点一个。

以上示例的最终结果如下:

                      ┌────────┬────┐  ┌────────┬────┐
                      │ weight │  0 │  │ weight │  0 │
                      ├────────┼────┤  ├────────┼────┤
                      │ y      │  2 │  │ y      │  1 │
                      ├────────┼────┤  ├────────┼────┤
                   ┌─►│ next   │  ────►│ next   │  - │
                   │  └────────┴────┘  └────────┴────┘
                   │       ┌────────┬────┐  ┌────────┬────┐
                   │       │ weight │  0 │  │ weight │  0 │
                   │       ├────────┼────┤  ├────────┼────┤
                   │       │ y      │  4 │  │ y      │  2 │
                   │       ├────────┼────┤  ├────────┼────┤
                   │    ┌─►│ next   │  ────►│ next   │  - │
                   │    │  └────────┴────┘  └────────┴────┘
                   │    │       ┌────────┬────┐
                   │    │       │ weight │  0 │
                   │    │       ├────────┼────┤
                   │    │       │ y      │  3 │
                   │    │       ├────────┼────┤
                   │    │    ┌─►│ next   │  - │
                   │    │    │  └────────┴────┘
                   │    │    │       ┌────────┬────┐
                   │    │    │       │ weight │  0 │
                   │    │    │       ├────────┼────┤
                   │    │    │       │ y      │  4 │
                   │    │    │       ├────────┼────┤
                   │    │    │    ┌─►│ next   │  - │
                   │    │    │    │  └────────┴────┘
    ┌───────────┬──│─┬──│─┬──│─┬──│─┬────┐    
g ─►│ edges     │  ┴ │  ┴ │  ┴ │  ┴ │  - │
    ├───────────┼────┼────┼────┼────┼────┤
    │ degree    │  2 │  2 │  1 │  1 │  0 │
    ├───────────┼────┼────┴────┴────┴────┘
    │ nvertices │  5 │ 
    ├───────────┼────┤
    │ nedges    │  6 │
    ├───────────┼────┤
    │ directed  │true│
    └───────────┴────┘

希望这样能澄清。

1
太详细的回答了!非常感谢! - VIAGC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接