5.2图的存储结构
图的存储结构相比线性表和树显得更复杂:
1)图中的顶点没有次序之分
2)图中边和顶点的数量任意
邻接矩阵:
无向图:
顶点:用数组存。 边或者弧:用二维数组来存储(二维数组也叫邻接矩阵)
优点:
1)判定两个顶点是否有边
2)求某个顶点的度
3)找到某个顶点的所有邻接点
4)无向图的邻接矩阵是一个对称矩阵,因此可以只存储上半部分矩阵
有向图:
优点:
1)顶点的入度是顶点所在这一列的个数之和;出度是顶点所在这一行的个数之和。
2)判定两个顶点是否有边
3)找到某个顶点的所有邻接点
1)带权边存储值
2)行和列相同结点权值为0
3)不存在变得权值无限大,在计算机存储中,不管任何类型,都有最大取值,用最大取值代表。
邻接矩阵结构:
#define MaxVertexNum 100 //顶点最大值
typedef char VertecType ; //顶点数据类型
typedef int EdgeType; //整数表示权值或者连通性
typedef struct{
VertecType Vex[MaxVertexNum];//顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum];
//邻接矩阵,边表
int vexnum, arcnum ; // 图的当前顶点数和弧数
}MGraph;
缺点:由于邻接矩阵基于二维数组,所以利用二维数组创建图的时间复杂度为O(n^2),其中n为顶点数,所以当顶点数较多时,邻接矩阵的复杂度也会较大。
对于稀疏图(|E|远小于|V|^2),浪费存储空间.。
用链式存储解决:
邻接表:
无向图:
有向图:
顶点为弧尾;链表叫做出边表
结构:
//顶点
typedef struct VNode{
VertexType data;//顶点数据;
ArcNode *firstedege; //单链表头指针
}VNode,AdjList[MaxVertexNum];
//AdjList是结构体数组类型
//单链表中结点
#define MaxVertexNum 100 //图中顶点属于穆最大值
typedef struct ArcNode{
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *next; //指向下一条弧的指针
}ArcNode;
typedef struct{
AdjList vertices; //邻接表
int vexnum,arcnum; //图中的顶点数和弧数
}ALGraph; //Algraph是 以邻接表存储的图类型
十字链表:
概念:在邻接表的基础上优化,不仅有出度结点还有入度结点。
由来:
邻接表解决了有向图的出边问题,我们通过有向图很容易找到顶点的出边比如从每个顶点表的firstedge指针找到第一条边的顶点,再通过next指针依次找到下一条边的顶点,再通过next指针找到下一条边的顶点直到空指针。
缺陷:
但是,如果要找到其他顶点到该顶点的边或者说考虑一个顶点的入度问题,则需要遍历整个图。
结构:
typdef struct ArcNode{
int tailvex,headvex; //弧尾所在顶点的下标和弧头所在顶点的下标
struct ArcNode *hlink,*tlink;
//指向弧头相同的下一条边和弧尾相同的下一条边
}ArcNode;
typedef struct VNode{
VertexType data; //顶点数据
ArcNode *firstin,*firstout; //指向入边表的头指针和出边表的头指针
}VNode;
typedef struct{
VNode xlist[MaxVertexNum]; //邻接表
int vexnum,arcnum; //图中的顶点数和弧数
}GLGraph; //Algraph是 以邻接表存储的图类型
邻接多重表
由来:邻接表存储的无向图中查找是比较容易的,但是如果要修改图中的边或者是查询边,则需要遍历链表。