图的邻接表存储结构
一般来说,图更多的是采用链表存储,具体的存储方法有 3 种,分别是邻接表、邻接多重表和十字链表
本篇文章将优先介绍邻接表!!!
邻接点:在图中,如果两个点相互连通,且通过其中一个顶点,可直接找到另一个顶点,则称它们互为邻接点
邻接:指图中顶点之间有边或者弧的存在
邻接表存储图的实现方式:给图中的各个顶点独自建立一个链表,用节点存储该顶点,用另一个链表中的节点存储其邻接点
特殊之处是,为了便于管理这些链表,通常会将链表的头节点存储到数组中,也正因为各个链表的头节点存储的是各个顶点,因此各链表在存储邻接点数据时,仅需存储该邻接点位于数组中的位置下标
图 1 邻接表存储有向图
如上图 1 中,给图的各个顶点创建了一个链表(用于存储数据域和头指针域),然后创建一个数组用来保存各个顶点;之后给邻接点又创建一个链表(用于存储邻接点在数组中的位置下标和指向下一个邻接点的指针)
对顶点 V1,与其相关的邻接点分别为 V2 和 V3,因此存储 V1 邻接点的链表中存储的是 V2 和 V3 在数组中的位置下标 1 和 2
图 1 中的链接表结构用结构体声明如下:
#define MAX_NUM 20 // 最大顶点个数
typedef enum{
DG, DN, UDG, UDN // 依次代表的值为 0,1,2,3
} GraphKind; // 枚举图的 4 种类型
typedef struct ArcNode{
int adjvex; // 邻接点在数组中的位置下标
struct ArcNode *nextarc; // 指向下一个邻接点的指针
int *info; // 信息域
} ArcNode;
typedef struct VNode{
int data; // 顶点的数据域
ArcNode *firstarc; // 指向邻接点的指针
} VNode, AdjList[MAX_NUM]; // 存储各链表头结点的数组
typedef struct{
AdjList vertices; // 图中顶点的数组
int vexnum, arcnum; // 记录图中顶点数和边或弧数
GraphKind kind; // 记录图的种类
} ALGraph;
邻接表计算顶点的出度和入度
无向图:使用邻接表计算无向图中顶点的入度和出度只需从数组中找到该顶点然后统计此链表中节点的数量即可
有向图:使用邻接表存储有向图时,通常各个顶点的链表中存储的都是以该顶点为弧尾的邻接点,因此通过统计各顶点链表中的节点数量,只能计算出该顶点的出度
对于(有向图)利用邻接表求某顶点的入度,有两种方式:
- 遍历整个邻接表中的节点,统计数据域与该顶点所在数组位置下标相同的节点数量,即为该顶点的入度
- 建立一个逆邻接表,该表中的各顶点链表专门用于存储以此顶点为弧头的所有顶点在数组中的位置下标
图 4 逆邻接表示意图