前言:
图无法以内存中的物理位置来表示元素之间的关系
1.图的存储结构(5种)
-
邻接矩阵(无向图)
图由顶点和边组成,用两种结构分别来存储顶点和边
顶点:不分主次,不分大小,可以用一维数组来存储
边or弧:由于边或弧是顶点与顶点之间的关系,可以用二维数组(称为邻接矩阵)来存储
- 那么可以设置两个数组,顶点一维数组vertex[4] = {V0, V1, V2, V3}, 边二维数组 arc[4][4] 为对称矩阵(0表示边不存在,1表示边存在)
- 如果两个顶点之间没有边,则二维数组值为0
- 要知道V1顶点的度,即为:顶点V1所在的二维数组的第i行或第i列元素和。
- 求顶点Vi 的所有邻接点,可以将二维数组的第i行扫描一遍,arc[i][j]为1就是邻接点
-
邻接矩阵(有向图)
- 那么可以设置两个数组,顶点一维数组vertex[4] = {V0, V1, V2, V3}, 边二维数组 arc[4][4] 为对称矩阵(0表示弧不存在,1表示弧存在)
- 顶点V1的入度为1:即第V1列的各数之和1
- 顶点V1的出度为1:即第V1行的各数之和2
-
邻接矩阵(网:每条弧上带权值的图就是网)
那么可以设置两个数组,顶点一维数组vertex[4] = {V0, V1, V2, V3}, 边二维数组 arc[4][4] 为对称矩阵(0表示弧不存在,8表示弧的权值, ∞表示大于所有弧上的权值,单向不存在弧)
-
邻接表(无向图)
- 把数组和链表结合一起来存储,这种方式在图中也适用,我们称为邻接表(AdjacencyList)
- 图中顶点用一个一维数组存储,顶点也可以用链表来存储,不过数组可以较为容易的读取信息
- 每个顶点Vi的所有邻接点构成一个线性表,由于邻接点的个数不确定,所以选择用单链表存储
-
代码结构
-
#define MaxSize 100 typedef struct Lnode //存放普通节点 { int t; Lnode* next; }; typedef struct Table //表头结构 { char data; Lnode* first; }; typedef struct AdList { Table adList[MaxSize];//存储顶点 int t; //下标 };
-
邻接表(有向图):以出度为基本
-
那么我们可以遍历链表,得到顶点Vi的出度
-
通过下标可以得知顶点之间的弧是否存在
-
逆邻接表(有向图):以入度为基本
-
图---十字链表表示(邻接表与逆邻接表的结合)
这个结构是用来表示 弧 的
那么就有:
- 十字链表的好处:将邻接表与逆邻接表整合在一起,可以容易的找到Vi为尾 or 为头 的弧,容易求出顶点的出度和入度
- 十字链表在数据结构中是非常好用的模型
#define MaxSize 100
typedef struct Hu //弧结构
{
int iV; //起始顶点所在的下标
int jV; //终点顶点所在的下标
Hu* headlink; //存放顶点下一个入度弧的地址
Hu *tailLink; //存放顶点下一个出度弧的地址
};
typedef struct Table //表头结构
{
int oV; // 起始顶点所在的下标
char data; //起始顶点的名称
Hu* headlink; //存放顶点下一个入度弧的地址
Hu *tailLink; //存放顶点下一个出度弧的地址
};
typedef struct AdList
{
Table adList[MaxSize];//存储顶点
int t; //下标
};
-
邻接多重表
- 存储结构
- iVex和jVex是与某条边依附的两个顶点在顶点表中的下标
- iLink指向依附顶点iVex的下一条边
- jLink指向依附顶点jVex的下一条边
-
边集数组
-
图的遍历(2种)
- 深度优先遍历
- 广度优先遍历
1.图的遍历--深度优先遍历(DFS: DepthFirstSearch):也叫深度优化搜索
有下图:约定右手原则,在没有碰到重复顶点的情况下,分岔路口始终是向右手边走,每过一个顶点就做好一个记号。
直到从A回退到A,证明所有的验证都已完毕了。先走一圈,把没有走顶点的都走一遍,如果没路可走,就往回退,确保每个顶点都走过了。深度优先遍历就是一个递归的过程