主要参考《数据结构》严蔚敏 吴伟民 编著 (C语言版)
图的表示法
- 邻接矩阵
用n*n
的矩阵方式表示顶点之间的关系,用1、0表示相邻与否。 - 邻接表
对于边/弧:
typedef struct ArcNode{
int adjvex; // 该弧指向的顶点的位置,在书中为顶点下标
struct ArcNode *nextarc; // 指示下一条弧的信息,该字段与弧无关,由顶点决定
InfoType *info; // 弧相关信息的指针,书本P164中的图应该是没有用到该字段
}ArcNode;
typedef struct VNode{
VertexType data; // 顶点信息
ArcNode *firstarc; // 指向第一条依附于该顶点的弧
}VNode, AdjList[size];
对比邻接矩阵,在边稀疏的情况下,邻接表比邻接矩阵更加节省空间。但是查询两顶点是否相邻,邻接矩阵更有优势。
还有逆邻接表,就是将弧方向转换。
3. 十字链表法(有向图)
顶点数据结构存放该顶点的第一条出弧
的指针和第一条入弧
的指针。
弧的数据结构存放该弧的弧头、弧尾顶点位置,以及下一条弧的指针,以此构成一张弧的链表。
弧个表的数据结构构成整个图。
4. 邻接多重表(无向图)
邻接表表示无向图会存在信息冗余。信息冗余必然会带来浪费以及数据的一致性问题。(想到数据库老师刚讲的信息冗余QAQ)
例如删除A->B的边,但是删除完成之后还要删除B->A的边,需要操作两次,增加操作,而且在复杂情况下容易出错。
顶点的数据结构仍然是数据和邻接的第一条边,和邻接矩阵相同。
边的数据结构修改为:
mark ivex ilink jvex jlink info
mark: 为标志域,可以用于标记该边是否被搜索过
ivex/jvex: 为弧两端顶点在图中的位置
ilink/jlink: 分别指向下一个邻接于ivex/jvex的弧
info: 弧信息
如此则实现多重邻接表。
图的搜索
深度优先搜索
和树相似,只不过要多加上对检测该顶点是否访问过
的检测。
递归思想即可,逻辑层面是类似栈的方式。
广度优先搜索
和树相似,只不过要多加上对检测该顶点是否访问过
的检测。
同样递归思想,逻辑为队列。
最小生成树
生成的所有边权值起来最小的树。
Prim算法
算法:
设置两个集合 v = 顶点集 和 u = {}
- 选择一个顶点a
v = 顶点集 - {a}
u = {a} - 从u集合的所有顶点的邻接顶点中,选择最近的顶点加入u(选择最小邻接边)
- 重复2直到所有的顶点都加入u
Kruskal算法
算法:
设置两个集合 v = 边集 和 u = {}
- 将所有的边按权值排序
- 选择v中权值最小的边加入u
加入的时候检测,该边相邻的两个顶点在u中边的作用下是否已经相邻,若相邻则舍弃该边,反之该边加入u - 重复2,直到v为空集
关键路径
算法思想:
- 求各个事件(节点)的最早时间
- 求各个事件(节点)的晚时间
- 求各个边的最早时间
- 求各个边的最晚时间
最早最晚时间相同的为关键边。
所有的关键边组成关键路径。
最短路径
Dijkstra算法
计算一个顶点到其余某个顶点的最短距离。
类似Prim算法。
算法:
设置两个集合 v = 顶点集 和 u = {}。
在设置一个path表,表示每个点到初始点的位置,初始化为无穷远。
- 将初始点加入 u,更新path表,将据相邻位置的顶点距离加入path表。
- 然后选择和u中顶点相邻的最近的顶点(v中的)加入u。
- 根据新入顶点,更新path表。
对于与新入点相邻的每一个点:- 若该点原本初始点不可达,即path[该点]值为无穷,则改为初始点到新入点距离+新入点到该点距离。
- 若该点原本可达,则修改为 path[该点] < 初始点到新入点距离+新入点到该点距离 ? 不变 : 初始点到新入点距离+新入点到该点距离。