图的定义
图G是一个有序二元组(V,E),其中V称为顶集(Vertices Set),表示图G中顶点的有限非空集;E称为边集(Edges set),表示图G中顶点之间的关系(边)集合。E与V不相交。
【注】线性表可以是空表,树可以是空树,但是图不可以是空图,也就是说图不可以一个顶点没有,图的顶点集V一定非空,但是边集E可以为空。
图的存储
1.邻接矩阵法
所谓邻接矩阵就是用一个一维数组存储图中的顶点信息,用一个二维数组存储图中边的信息(即各顶点之间的邻接关系),存储顶点之间邻接关系的二维数组成为邻接矩阵。
结点数为 n 的图G=(V,E)的邻接矩阵A是 n×n 的,将G的顶点编号为,若(
,
…
)
E,则A[ i ][ j ] = 1,否则A [ i ] [ j ] = 0;
对于带权图来说,若顶点
和
之间有边相连,则邻接矩阵中对应项存放着改变对应的权值,若顶点
和
不相连,则用 ∞ 来代表这两个顶点之间不存在边。
有向图和无向图对应的邻接矩阵举例如下:
图的邻接矩阵存储结构定义如下:
#define MaxVertexNum 100 //顶点最大值为100
typedef cha VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型
typedef struct{
VertexType Vex[MaxVertexNum]; //顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, arcnum; //图的当前顶点数和弧数
}MGraph;
【注】1.无向图的邻接矩阵是对称的,对规模较大的邻接矩阵可以采用压缩存储;
2.邻接矩阵表示法的空间复杂度为O(
),其中n为图的顶点数|V|;
2.邻接表法
从上面我们可以看出当一个图为稀疏图时,使用邻接矩阵法显然要浪费大量的存储空间,而图的邻接表法结合了顺序存储和链式存储,大大减少了这种不必要的浪费。
所谓邻接表就是指图中G的每个顶点
建立一个单链表,第 i 个单链表中的结点表示依附于顶点
的边(对于有向图则是以顶点
为尾的弧),这个单链表就称为顶点
的边表(对于有向图则称为出边表)。边表的头指针和顶点的信息采用顺序存储(称为顶点表),所以在邻接表中存在两种结点:顶点表结点和边表结点;
如下图所示:
顶点表结点由顶点域(data)和指向第一条邻接边的指针(firstac)构成;
边表(邻接表)结点由邻接点域(adjvex)和指向下一条邻接边的指针域(nextarc)构成;
无向图和有向图对应的邻接表举例如下:
1.无向图邻接表表示法实例:
2.有向图邻接表表示法实例:
图的邻接表存储结构定义如下:
#define MaxVertexNum 100 //定义顶点最大值为100
typedef struct ArcNode{ //边表结点
int adjvex; //该弧所指向的顶点位置
struct ArcNode *next; //指向下一条弧的指针
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该结点的弧的指针
}ANode,AdjList[MaxVertexNum];
typedef struct{ //邻接表
AdjList vertices; //图的顶点数和弧数
int vexnum,arcnum; //ALGraph是以邻接表存储的图类型
}
【注】图的邻接表不唯一,因为在每个顶点对应的单链表中,各边结点的连接次序可以是任意的,它取决于建立邻接表的算法及边的输入次序。
3.十字链表
十字链表是有向图的一种链式存储结构,在十字链中,对应于有向图的每条弧有一个结点,对应于每一个结点也有一个结点,这些结点的结构如下图:
弧结点中有5个域:尾域(tailvex)和头域(headvex)分别指示弧尾和弧头这两个顶点在图中的位置:链域 hlink 指向弧头相同的下一条弧;链域 tlink 指向弧尾相同的下一条弧;info域指向该弧相关信息;这样的话,弧头相同的弧就在同一个链表上,弧尾相同的弧也在同一链表上。
顶点结点中有3个域:data域中存放顶点相关的数据信息,如顶点名称;firstin 和 firstout 两个域分别指向该顶点为弧头或弧尾的第一个弧结点。
有向图的十字链表举例如下:
【注】图的十字链表表示是不唯一的,但一个十字链表所表示的图是确定的;
十字链表的存储结构定义如下:
#define MaxVertexNum 100 //定义顶点最大值为100
#define InfoType int //图中弧包含信息的数据类型
#define VertexType int
typedef struct ArcBox{
int tailvex,headvex; //弧尾、弧头对应顶点在数组中的位置下标
struct ArcBox *hlik,*tlink; //分别指向弧头相同和弧尾相同的下一个弧
InfoType *info; //存储弧相关信息的指针
}ArcBox;
typedef struct VexNode{
VertexType data; //顶点的数据域
ArcBox *firstin,*firstout; //指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;
typedef struct {
VexNode xlist[MaxVertexNum]; //存储顶点的一维数组
int vexnum,arcnum; //记录图的顶点数和弧数
}OLGraph;
4.邻接多重表
邻接多重表是无向图的另一种链式存储结构;与十字链表类似,在邻接多重表中,每一条边用一个结点表示,其结构如下所示:
其中,mark为标志域,可以用来 标记该条边是否被搜索过;ivex和jvex为该边依附的两个顶点在图中的位置;ilink指向下一条依附于顶点ivex的边;jlink指向下一条依附于顶点jvex的边;info为指向和边相关的各种信息的指针域;
其中。data域存储该顶点的相关信息,firstedge域指示第一条依附于该顶点的边;
无向图的邻接多重表表示法举例如下:
【注】我们需要注意的是在邻接多重表中,所有依附于同一顶点的边串联在同一链表中,由于每条边依附于两个顶点,因此每个边结点同时链接在两个链表中,对于无向图而言,其邻接多重表和邻接表的差别仅仅在于同一条边在邻接表中用两个结点表示,而在多重表中只有一个结点;
邻接多重表的存储结构定义如下:
#define MaxVertexNum 100 //定义顶点最大值为100
#define InfoType int //图中弧包含信息的数据类型
typedef enum { //类型定义语句
unvisited,visited
} VisitIF;
typedef struct EBox{
VisitIf mark; // 访问标记
int ivex,jvex; // 该边依附的两个顶点的位置
EBox *ilink,*jlink; // 分别指向依附这两个顶点的下一条边
InfoType *info; // 该边信息指针,可指向权值或其它信息
}EBox;
typedef struct VexBox{
VertexType data;
EBox *firstedge; // 指向第一条依附该顶点的边
}VexBox;
typedef struct AMLGraph{
VexBox adjmulist[MaxVertexNum];
int vexnum,edgenum; // 无向图的当前顶点数和边数
}AMLGraph;