第七章 图
知识点
-
掌握:图的基本概念及相关术语和性质
-
图的定义
- 图:Graph=(V,E)
- V:顶点(数据元素)的有穷非空集合;
- E:边的有穷集合。
-
图的分类
- 有向图
- 无向图
- 网络
-
图的相关术语
-
邻接:有边/弧相连的两个顶点之间的关系。
- 存在(vi, vj),则称vi和vj互为邻接点;
- 存在<vi, vj>,则称vi邻接到vj,vj邻接于vi
-
关联(依附):边/弧与顶点之间的关系。
- 存在(vi, vj)/ <vi, vj>, 则称该边/弧关联于vi和vj
-
边的数目达到最大的图称为
完全图
。 -
边的数目达到或接近最大的图称为
稠密图
,否则,称为稀疏图
。 -
顶点的度:与该顶点相关联的边的数目,记为TD(v)
- 在有向图中, 顶点的度等于该顶点的入度与出度之和。
- 顶点 v 的入度是以 v 为终点的有向边的条数, 记作 ID(v)
- 顶点 v 的出度是以 v 为始点的有向边的条数, 记作OD(v)
-
路径:接续的边构成的顶点序列。
-
路径长度:路径上边或弧的数目/权值之和。
-
回路(环):第一个顶点和最后一个顶点相同的路径。
-
简单路径:除路径起点和终点可以相同外,其余顶点均不相同的路径。
-
简单回路(简单环):除路径起点和终点相同外,其余顶点均不相同的路径。
-
连通图(强连通图)
在无(有)向图G=( V, E )中,若对任何两个顶点 v、u 都存在从v 到 u 的路径,则称G是连通图(强连通图)。 -
子图
设有两个图G=(V,{E})、G1=(V1,{E1}),若V1是V的子集,E1是E的子集,则称 G1是G的子图 -
连通分量(强连通分量)
- 无向图G 的极大连通子图称为G的连通分量。
- 极大连通子图意思是该子图是 G 连通子图,将G 的任何不在该子图中的顶点加入,子图不再连通。
- 有向图G 的极大强连通子图称为G的强连通分量。
- 极大强连通子图意思是该子图是G的强连通子图,将G的任何不在该子图中的顶点加入,子图不再是强连通的。
-
极小连通子图:该子图是G 的连通子图,在该子图中删除任何一条边,子图不再连通。
-
生成树:包含无向图G 所有顶点的极小连通子图。
-
生成森林:对非连通图,由各个连通分量的生成树的集合。
-
-
图的性质
- 对于具有n个顶点,e条边的图有 e = d/2
- 具有n个顶点的无向图最多有n(n-1)/2条边。
- 具有n个顶点的有向图最多有n(n-1)条边。
-
-
熟练掌握:图的邻接矩阵和邻接表两种存储表示方法
-
邻接矩阵
-
建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间关系)。
设图 A = (V, E ) 有 n 个顶点,则图的邻接矩阵是一个二维数组 A.Edge[n][n]。 -
邻接矩阵的存储表示
//用两个数组分别存储顶点表和邻接矩阵 #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;
-
分析1:无向图的邻接矩阵是对称的;
分析2:顶点i
的度 = 第i
行 (列) 中1
的个数;
特别:完全图的邻接矩阵中,对角元素为0
,其余1
。 -
分析1:有向图的邻接矩阵可能是不对称的。
分析2:顶点的出度 = 第
i
行元素之和
顶点的入度 = 第i
列元素之和
顶点的度 = 第i
行元素之和 + 第i
列元素之和 -
特点
- 优点:容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边、找顶点的邻接点等等。
- 缺点:n个顶点需要n*n个单元存储边;空间效率为O(n2)。 对稀疏图而言尤其浪费空间。
-
-
邻接表
- 算法思想
(1)输入总顶点数和总边数。
(2)依次输入点的信息存入顶点表中。
(3)初始化邻接矩阵,使每个权值初始化为极大值。
(4)构造邻接矩阵。-
邻接表的存储表示
#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;
-
无向图的邻接表表示
- 空间效率为O(n+2e)。
- 若是稀疏图(e<<n2),比邻接矩阵表示法O(n2)省空间。
-
有向图的邻接表表示
空间效率为O(n+e) -
特点
- 优点:空间效率高,容易寻找顶点的邻接点;
- 缺点:判断两顶点间是否有边或弧,需搜索两结点对应的单链表,没有邻接矩阵方便。
-
邻接矩阵与邻接表表示法的关系
-
联系:邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数。
-
区别:
- 对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。
- 邻接矩阵的空间复杂度为O(n2),而邻接表的空间复杂度为O(n+e)。
-
用途:邻接矩阵多用于稠密图;而邻接表多用于稀疏图
-
-
-
熟练掌握:图的两种遍历方法DFS和BFS
-
深度优先搜索( DFS - Depth_First Search)
-
步骤
- 简单归纳:
- 访问起始点v;
- 若v的第1个邻接点没访问过,深度遍历此邻接点;
- 若当前邻接点已访问过,再找v的第2个邻接点重新遍历。
-
分析
-
用邻接矩阵来表示图,遍历图中每一个顶点都要从头扫描该顶点所在行,时间复杂度为O(n2)。
用邻接表来表示图,虽然有 2e 个表结点,但只需扫描 e 个结点即可完成遍历,加上访问 n个头结点的时间,时间复杂度为O(n+e)。 -
结论:
- 稠密图适于在邻接矩阵上进行深度遍历;
- 稀疏图适于在邻接表上进行深度遍历。
-
-
-
广度优先搜索( BFS - Breadth_First Search)
-
步骤
- 简单归纳:
- 在访问了起始点v之后,依次访问 v的邻接点;
- 然后再依次访问这些顶点中未被访问过的邻接点;
- 直到所有顶点都被访问过为止。
-
算法思想
-
从图中某个顶点v出发,访问v,并置visited[v]的值为true,然后将v进队。
-
只要队列不空,则重复下述处理。
- 队头顶点u 出队。
- 依次检查u 的所有邻接点w,如果visited[w]的值为false,则访问w,并置visited[w]的值为true,然后将w进队。
-
-
效率分析
- 如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行( n 个元素),总的时间代价为O(n2)。
- 用邻接表来表示图,虽然有 2e 个表结点,但只需扫描 e 个结点即可完成遍历,加上访问 n个头结点的时间,时间复杂度为O(n+e)。
-
-
比较
- 空间复杂度相同,都是O(n)(借用了堆栈或队列);
- 时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。
-
-
熟练掌握:最短路算法(Dijkstra算法)
-
思想
- 初始化:先找出从源点v0到各终点vk的直达路径(v0,vk),即通过一条弧到达的路径。
- 选择:从这些路径中找出一条长度最短的路径(v0,u)。
- 更新:然后对其余各条路径进行适当调整:
若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk),则以路径(v0,u,vk)代替(v0,vk)。
在调整后的各条路径中,再找长度最短的路径,依此类推。
-
-
掌握:最小生成树的两种算法及拓扑排序算法的思想
- Prim算法: 归并顶点,与边数无关,适于稠密网
- Kruskal算法:归并边,适于稀疏网
习题
-
画出下图所示的无向图的邻接表,使得其中每个无向边结点中第一个顶点号小于第二个顶点号,且每个顶点的各邻接边的链接顺序,为它所邻接到的顶点序号由小到大的顺序。列出深度优先和广度优先搜索遍历该图所得顶点序列和边的序列。
-
已知以二维数组表示的图的邻接矩阵如下图所示。试分别画出自顶点1出发进行遍历所得的深度优先生成树和广度优先生成树。
-
请对下图的无向带权图,
(1)写出它的邻接矩阵,并按普里姆算法求其最小生成树;
(2)写出它的邻接表,并按克鲁斯卡尔算法求其最小生成树。 -
试利用Dijkstra算法求下图中从顶点a到其他各顶点间的最短路径,写出执行算法过程中各步的状态。
-
请给出下图的最小生成树,要求给出其过程。
-
请补充完整下表,完成从A点到其他各个顶点的最短路径及其路径长度。
终点 路径 路径长度
A->B AB 5
A->C AC 3
A->D
A->E
A->F
A->G ABG 6