图的邻接表表示法类似于树的孩子链表表示法。对于图G中的每个顶点vi,该方法把所有邻接于vi的顶点vj链成一个带头节点的单链表,这个单链表就称为顶点vj的邻接表。
单链表中的每个结点至少包含两个域,一个为邻接点域(adjvex),它指示与顶点vi邻接的顶点在图中的位序;另一个为链域(next),它指示与顶点vi邻接的下一个节点。如果是网络,可以在单链表的头节点中增加一个数据域用于存储和边(弧)相关的信息,如权值等。在每个链表上需附设一个表头结点,在表头结点中,除了设有头指针域(firstedge)指向链表中的第一个结点之外,还设有存储顶点vi的数据域(vertex)或其他有关信息的数据域。
头结点与链表结点的结构可表示为:
对于无向图,vi的邻接表中每个表结点都对应于与vi相关联的一条边;
例如,对于如图所示的无向图:
它的邻接表如图所示:
建立思想(输入顶点对(a,b)表示一条连接着两个顶点的边):
先将b所在结点连接到a所在结点的后面,再将a所在结点连接到b所在结点的后面:
对于有向图,如果每一顶点vi的邻接表中每个表结点都存储以vi为始点射出的一条边,则称这种图为有向图的出边表(邻接表);反之,若每一顶点vi的邻接表中每个表结点都对应于以vi为终点的边(即射入vi的边),则称这种表为有向图的入边表(逆邻接表)。
例如,对于如图所示的有向图:
出边表(邻接表):
建立思想(输入顶点对(a,b)表示一条连接着两个顶点的边):
将b所在结点连接到a所在结点的后面(与无向图相比只有第一步):
入边表:
邻接表的建立:
邻接表存储结构:
#include<stdio.h>
#include<stdlib.h>
#define M 20 //图的最大顶点数
typedef struct node { //边表结点
int adjvex; //邻接点
struct node* next;
}EdgeNode;
typedef struct vnode { //头结点类型
char vertex; //顶点信息
EdgeNode* FirstEdge; //邻接表头指针
}VertexNode;
typedef struct { //邻接表类型
VertexNode adjlist[M]; //存放头结点的顺序表
int n, e; //图的顶点数与边数
}LinkedGraph;
邻接表建立算法:
void create(LinkedGraph* g, int c) {
//c为0表示建立无向图,否则表示建立有向图
int a, b;
EdgeNode* s;
scanf_s("%d", &g->n); //顶点数
scanf_s("%d", &g->e); //边数
for (int i = 0; i < g->n; i++) {
scanf_s("%s", g->adjlist[i].vertex,20); //输入顶点信息
g->adjlist[i].FirstEdge = NULL; //边表置空
}
for (int j = 0; j < g->e; j++) { //建立边表
scanf_s("%d%d", &a, &b); //输入无序对(a,b)表示边的顶点对
//有向图(出边表)
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = b; //邻接点序号为b
s->next = g->adjlist[a].FirstEdge;
g->adjlist[a].FirstEdge = s; //将新的节点*s插入到顶点Vi的边表头部
if (c == 0) {
//无向图
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = a; //邻接点序号为a
s->next = g->adjlist[b].FirstEdge;
g->adjlist[b].FirstEdge = s; //将新的节点*s插入到顶点Vi的边表头部
}
}
}
建立好图的邻接表后,我们将其命名为”ljb.h":
/*ljb.h*/
#include<stdio.h>
#include<stdlib.h>
#define M 20 //图的最大顶点数
typedef struct node { //边表结点
int adjvex; //邻接点
struct node* next;
}EdgeNode;
typedef struct vnode { //头结点类型
char vertex; //顶点信息
EdgeNode* FirstEdge; //邻接表头指针
}VertexNode;
typedef struct { //邻接表类型
VertexNode adjlist[M]; //存放头结点的顺序表
int n, e; //图的顶点数与边数
}LinkedGraph;
void create(LinkedGraph* g, int c) {
//c为0表示建立无向图,否则表示建立有向图
int a, b;
EdgeNode* s;
scanf_s("%d", &g->n); //顶点数
scanf_s("%d", &g->e); //边数
for (int i = 0; i < g->n; i++) {
scanf_s("%s", &g->adjlist[i].vertex, 20); //输入顶点信息
g->adjlist[i].FirstEdge = NULL; //边表置空
}
for (int j = 0; j < g->e; j++) { //建立边表
scanf_s("%d%d", &a, &b); //输入无序对(a,b)表示边的顶点对
//有向图(出边表)
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = b; //邻接点序号为b
s->next = g->adjlist[a].FirstEdge;
g->adjlist[a].FirstEdge = s; //将新的节点*s插入到顶点Vi的边表头部
if (c == 0) {
//无向图
s = (EdgeNode*)malloc(sizeof(EdgeNode));
s->adjvex = a; //邻接点序号为a
s->next = g->adjlist[b].FirstEdge;
g->adjlist[b].FirstEdge = s; //将新的节点*s插入到顶点Vi的边表头部
}
}
}
接下来我们就可以基于这个邻接表对图进行遍历了:
基于图的邻接表的深度优先遍历(DFS)和广度优先遍历(BFS)