图的存储及基本操作
图的存储必须要完整、准确地反映 顶点集 和 边集 的信息。
根据不同图的结构和算法,采用不同的存储方式将对程序的效率产生相当大的影响,因此所选的存储结构应适合于欲求解的问题。
邻接矩阵法
用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息(即各个顶点之间的关系),存储顶点之间邻接关系的二维数组。
代码实现:
#define MaxVertexNum 100 //一维数组的最大空间
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型
//图的邻接矩阵存储结构定义
typedef struct {
VertexType Vex[MaxVertexNum]; //顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, arcnum; //图的当前顶点数和边数
}MGraph;
①在一般情况下,可直接用二维数组作为图的邻接矩阵,可忽略顶点表。
②当判断两个顶点构成的边是否存在时,可以访问Edge二维数组,0为不存在,1为存在。
③邻接矩阵表示法的空间复杂度为O(n^2),n为顶点数,这是因为图的邻接矩阵需要用二维数组来存储。
④对于无向图,邻接矩阵 就是 对称矩阵,因此在实际存储的过程中,只需存储上(或下)三角矩阵的元素。顶点的度数就是邻接矩阵中顶点所在行(或列)的非0元素个数。
⑤对于有向图,邻接矩阵的第i行的非0元素个数是这个顶点的出度,第i列的非0元素个数是这个顶点的入度。
⑥用邻接矩阵法存储图,很容易确定图中任意两个顶点之间是否右边相连。但是,要确定图中有多少边,则必须按行、按列对每个元素进行检测,所花费的时间代价很大。所以,稠密图适合使用邻接矩阵的存储表示,而稀疏图不合适。
邻接表法
对图中的每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边。图的邻接表法结合了顺序存储和链式存储的方法,大大减少了存储空间的浪费。
代码实现:
#define MaxVertexNum 100 //顶点数的最大值
typedef char VertexType; //顶点的数据类型
//边表结点
typedef struct ArcNode {
int adjvex; //该弧所指向顶点的位置
struct ArcNode* next; //指向下一条弧的指针
}ArcNode;
//顶点表结点
typedef struct VNode {
VertexType data; //顶点信息
ArcNode* first; //指向第一条依附该顶点的弧的指针
}VNode, AdjList[MaxVertexNum]; //创建一个数组AdjList,里面存放顶点集
//以邻接表存储的结构体
typedef struct {
AdjList vertices; //邻接表
int vexnum, arcnum; //图的顶点数和弧数
}ALGraph;
①对于无向图,每条边在邻接表出现了2次,所以空间复杂度是O(|V|+2|E|)。对于有向图,空间复杂度为O(|V|+|E|)。
②在邻接表中,给定一个顶点,找到其所有邻边,只需读取它的邻接表,因此花费的时间是O(n)。但是,想要确定两个顶点间是否存在边,需要在相应结点对应的边表中查找另一个结点,效率较低。
③图的邻接表不唯一,各个边界点的链接次序可以是任意的。
除了上述两种方法存储图之外,还有十字链表和邻接多重表来存储图。在此就不一一赘述了。