基础概念
节点(node):如果某些事物具有相同或相似的属性,则可以抽象为节点。例如地图上的各个建筑,一个局域网内的各个设备等等,都可以抽象为一个个的节点 x i x_i xi。
边(edge):连接由一个节点(出发节点)指向另一个节点(到达节点)的路径称之为有向边,这条边也称之为做出发节点的出边,或者到达节点的入边。一条有向边必须包含出发节点 u u u和到达节点 v v v,抽象为有序数对 ( u , v ) (u,v) (u,v)。此外边还可以包含路径长度等更多的信息。
图(graph):由点集 U = { x i } U=\{x_i\} U={xi}和边集合 V = { ( u i , v i ) } V=\{(u_i,v_i)\} V={(ui,vi)}构成的整体。
下面图片即可直观地表示一幅 6 6 6个节点, 5 5 5条边所构成的有向图:
图的存储
如何实现一张图的存储呢?什么一个数据结构,可以实现任意一点 x x x的所有出边的遍历,以及高效插入新的出边?显然,链表非常适合这份工作。
将每条边抽象为二元组 ( x , v e r t e x ) (x,vertex) (x,vertex), x x x为出发节点, v e r t e x vertex vertex为到达节点。
typedef struct Edge{
int vertex;
struct Edge *next;
}Edge;
对于每一个节点 x x x,建立一个 E d g e Edge Edge结构体单向链表,链表中的节点表示 x x x的出边的信息,即到达节点,边的长度等等。此外为方便快速存储,对每个节点 x x x建立一个 E d g e Edge Edge结构体指针,表示 x x x链表的表头。下列为边的存储、节点出边的遍历两个操作的参考实现。
Edge *head[N];
void add(int x, int y) { //从x指向y的边
Edge *New = malloc(sizeof(Edge));
New->vertex = y;
New->next = head[x];
head[x] = New;
}
void visit(int x) { //访问并输出x指向的所有节点
for (Edge *Now = head[x]; Now != NULL; Now = Now->next) {
printf("%d ", Now->vertex);
}
}