数据结构与算法之图的应用
图的定义和基本概念
- 在图中,通常将数据元素称为顶点(Vertex) ,数据元素之间的关系称为边(Edge) 。
- 图(Graph)由有限顶点集V和表示顶点之间关系的有限边集E组成,记为: G = ( V , E ) G= (V,E) G=(V,E)
- 其中:顶点总数|V|记为n,边的总数|E|记为e
- 无向图:每条边都是无方向的
- 有向图:每条边都是有方向的
- 完全图:任意两个点都有一条边相连
- 顶点的度:与该顶点相关联的边的数目,记为TD(v)
- 在有向图中, 顶点的度等于该顶点的入度与出度之和。
顶点 v 的入度是以 v 为终点的有向边的条数, 记作 ID(v)
顶点 v 的出度是以 v 为始点的有向边的条数, 记作OD(v)- 路径:接续的边构成的顶点序列。
- 路径长度:路径上边或弧的数目/权值之和。
- 回路(环):第一个顶点和最后一个顶点相同的路径。
- 简单路径:除路径起点和终点可以相同外,其余顶点均不相同的路径。
- 简单回路(简单环):除路径起点和终点相同外,其余顶点均不相同的路径。
- 连通图(强连通图)
在无(有)向图G=( V, {E} )中,若对任何两个顶点 v、u 都存在从v 到 u 的路径,则称G是连通图(强连通图)。
- 权与网
图中边或弧所具有的相关数称为权。
表明从一个顶点到另一个顶点的距离或耗费。带权的图称为网。- 子图。如下图:( b )、( c ) 是 ( a ) 的子图。
图的实现
数组〈邻接矩阵〉
- 无向图的邻接矩阵表示法
- 有向图的邻接矩阵表示法
- 网(即有权图)的邻接矩阵表示法
- 优点:
容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边、找顶点的邻接点等等。 - 缺点:
n个顶点需要n*n个单元存储边;空间效率为 O ( n 2 ) O(n^2) O(n2)。 对稀疏图而言尤其浪费空间。
//用两个数组分别存储顶点表和邻接矩阵
#define MaxInt 32767 //表示极大值,即∞
#define MVNum 100 //最大顶点数
typedef char VerTexType; //假设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
typedef struct{
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum; //图的当前点数和边数
}AMGraph;
邻接表
- 无向图的邻接表表示
注:邻接表不唯一,因各个边结点的链入顺序是任意的
空间效率为O(n+2e)。
若是稀疏图 ( e < < n 2 ) (e<<n^2) (e<<n2),比邻接矩阵表示法 O ( n 2 ) O(n^2) O(n2)省空间。 - 有向图的邻接表表示
注:空间效率为O(n+e)
出度:OD(Vi)=单链出边表中链接的结点数
入度:ID(Vi)=邻接点域为Vi的弧个数
度:TD(Vi) = OD(Vi) + I D(Vi) - 优点:
空间效率高,容易寻找顶点的邻接点; - 缺点:
判断两顶点间是否有边或弧,需搜索两结点对应的单链表,没有邻接矩阵方便。
#define MVNum 100 //最大顶点数
typedef struct ArcNode{ //边结点
int adjvex; //该边所指向的顶点的位置
struct ArcNode * nextarc; //指向下一条边的指针
OtherInfo info; //和边相关的信息
}ArcNode;
typedef struct VNode{
VerTexType data; //顶点信息
ArcNode * firstarc; //指向第一条依附该顶点的边的指针
}VNode, AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct{
AdjList vertices; //邻接表
int vexnum, arcnum; //图的当前顶点数和边数
}ALGraph;
图的应用
最小生成树
Prim(普里姆)算法
图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克发现;并在1957年由美国计算机科学家罗伯特·普里姆独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
Kruskal(克鲁斯卡尔)算法
克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同,它的时间复杂度为 O ( e l o g 2 e ) O(elog_2e) O(elog2e)(e为网中的边数),所以,适合于求边稀疏的网的最小生成树。
最短路径
- 最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:
- 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。
- 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
- 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
- 全局最短路径问题 - 求图中所有的最短路径。
迪克斯特拉算法
- 迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
- 基本思想:
1.初始化:先找出从源点v0到各终点vk的直达路径(v0,vk),即通过一条弧到达的路径。
2.选择:从这些路径中找出一条长度最短的路径(v0,u)。
3.更新:然后对其余各条路径进行适当调整:
若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk),则以路径(v0,u,vk)代替(v0,vk)。
在调整后的各条路径中,再找长度最短的路径,依此类推。- 例如,
最短路径为{v0 ,v2 ,v4 ,v3 ,v5}
拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
执行步骤
由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
- (1) 选择一个入度为0的顶点并输出之;
- (2) 从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。
例如,
最后得到拓扑序列C4 , C0 , C3 , C2 , C1 , C5
关键路径
- 关键路径是指设计中从输入到输出经过的延时最长的逻辑路径。优化关键路径是一种提高设计工作速度的有效方法。一般地,从输入到输出的延时取决于信号所经过的延时最大路径,而与其他延时小的路径无关。在优化设计过程中关键路径法可以反复使用,直到不可能减少关键路径延时为止。
- AOE-网(Activity On Edge)即边表示活动的网。AOE-网是一个带权的有向无环图,其中,顶点表示事件(Event),弧表示活动,权表示活动持续的时间。通常,AOE-网可用来估算工程的完成时间。
- 一个AOE网,如下图,
- 源点:表示整个工程的开始点,也称起点。
- 收点:表示整个工程的结束点,也称汇点。
- 事件结点:单位时间,表示的是时刻。
- 活动(有向边):它的权值定义为活动进行所需要的时间。
方向表示起始结点事件先发生,而终止结点事件才能发生。- 关键活动:最早开始时间 = 最迟开始时间的活动
- 关键路径:从源点到收点的最长的一条路径,或者全部由关键活动构成的路径。
- 四个主要描述量
例如,已知AOE网中顶点v1,v2,v3,…v7分别表示7个时间,有向线段a1,a2,a3,…a10分别表示10个活动,线段旁的数值表示每个活动花费的天数,如下图所示。用顶点序列表示出关键路径,给出关键活动。
解析:
故关键路径:
(1)V1->V2->V5->V7
(2)V1->V4->V5->V7
注意:关键路径可有多条缩短工期必须缩短关键活动所需的时间
参考文献
[1] 严蔚敏,吴伟民. 数据结构(C语言版). 北京: 清华大学出版社,2020
[2] 严蔚敏,李冬梅,吴伟民. 数据结构(C语言版)(第二版). 北京: 人民邮电出版社,2021
[3] 吴伟民,李小妹,刘添添,黄剑锋,苏庆,林志毅,李杨.数据结构. 北京:高等教育出版社,2017