1.1k likes | 1.27k Views
第 7 章 图. wangzs@vip.sina.com.cn. 学习要点. 图的定义和基本术语 图的存储结构 图的遍历 图的连通性问题 有向无环图及其应用 最短路径. 重点:理解各种图的算法及其应用场合. 1 )画出 T 型路口交通管理示意图 2 )每次让三条路同时通行,那么从图看出哪些路可以同时通行. 7.1 图的定义和术语. ADT Graph{ 数据对象 V : 顶点集,是具有相同特性的数据元素的集合 数据关系 R : R={ VR }
E N D
第7章 图 wangzs@vip.sina.com.cn
学习要点 • 图的定义和基本术语 • 图的存储结构 • 图的遍历 • 图的连通性问题 • 有向无环图及其应用 • 最短路径 重点:理解各种图的算法及其应用场合
1)画出T型路口交通管理示意图 2)每次让三条路同时通行,那么从图看出哪些路可以同时通行
7.1 图的定义和术语 ADT Graph{ 数据对象V:顶点集,是具有相同特性的数据元素的集合 数据关系R: R={ VR } VR={<v,w>|v,w∈V且P(v,w),<v,w>表示从v到w的弧,谓词p(v,w)定义了弧<v,w>的意义或信息 } G1=(V1, VR1)其中:V1 = {A, B, C, D, E} VR1 ={<A,B>,<A,E>,<B,C>,<C,D>,<D,B>,<D,A>,<E,C>}
图的定义和术语 若弧<v,w>∈R 必有<w,v>∈R,则称 (v,w) 为顶点 v 和顶点 w 之间存在一条边。由顶点集和边集构成的图称作无向图。如上图描述为: G2=(V2, VR2) 其中: V2 ={ A, B, C, D, E, F } VR2={(A,B),(A,E),(B,E),(C,D),(D,F),(B,F),(C,F) }
图的定义 基本操作 P: CreateGraph(&G,V,VR); DestroyGraphi(&G); GetVex(G,v); LocateVex(G, u); FirstAdjVex(G, v); NextAdjVex(G, v, w); PutVex(&G, v, value); InsertVex(&G, v); DeleteVex(&G, v); InsertArc(&G, v, w); DeleteArc(&G, v, w); DFSTraverse(G, Visit()); BFSTraverse(G, Visit()); }ADT Graph
图的术语 无向图边的数目e的取值范围是:0-n*(n-1)/2 有向图的弧的数目e的取值范围是0-n*(n-1) • 完全图:有n*(n-1)/2条边的无向图 • 有向完全图:有n*(n-1)条弧的有向图 • 稀疏图与稠密图:边或弧e<n*logn的图, 反之称为稠密图 • 权:图的边或弧具有与它相关的数 • 网:带权的图 • 子图:两个图G=(V,{E})和G’=(V’,{E’}) V’是V的子集,{E’}是{E}的子集,则G’是G的子图
图的术语 例 2 4 5 1 3 6 2 2 1 3 1 3 5 3 6 例 无向完全图 有向完全图 图与子图
图的术语 路径和回路 有向路径:若有向图 G 中 k+1 个顶点之间都有弧存在,则这个顶点的序列为从顶点v0到顶点vk的一条有向路径。 无向路径:无向图,相邻顶点之间存在边的 k+1 个顶点序列构成一条长度为 k 的无向路径。 路径长度:路径中弧的数目。 简单路径:序列中的顶点都不相同。 回路或环:如果v0 和vk是同一个顶点,则是一条由某个顶点出发又回到自身的路径。 √ √
图的术语 2 4 5 1 3 6 G1 1 5 7 3 2 4 6 G2 例 路径:1,2,3,5,6,3 路径长度:5 简单路径:1,2,3,5 回路:1,2,3,5,6,3,1 简单回路:3,5,6,3 例 路径:1,2,5,7,6,5,2,3 路径长度:7 简单路径:1,2,5,7,6 回路:1,2,5,7,6,5,2,1 简单回路:1,2,3,1
图的术语 例 2 4 5 1 3 6 G1 例 1 5 7 3 2 4 6 G2 顶点的度 无向图,邻接点的个数。 有向图,出度和入度之和,其中出度定义为以该顶点为弧尾的弧的个数,入度定义为以该顶点为弧头的弧的个数。 顶点5的度:3 顶点2的度:4 顶点2入度:1 出度:3 顶点4入度:1 出度:0
图的术语 • 连通图和连通分量、强连通图和强连通分量 连通图:无向图中任意两个顶点之间存在一条无向路径 强连通图:有向图中任意两个顶点之间都存在一条有向路径。 连通分量:非连通图中各个极大连通子图。 强连通分量:有向图的极大强连通子图。
图的术语 例 2 4 5 1 3 6 例 例 2 4 5 5 1 3 6 3 6 连通图 强连通图 非连通图 强连通连通分量
图的术语 生成树--连通图的极小连通子图,它含有图中全部顶 点,但只有足以构成一棵树的n-1条边。 有向树--有向图恰有一个顶点的入度为0,其余顶点的 入度都为1 生成森林--有向图的生成森林由若干棵有向树组成, 含有图中全部顶点,但只有足以构成若干棵不相交的有 向树的弧
图的存储结构--多重链表 图没有顺序存储结构
图的存储结构--多重链表 例 V3 ^ V4 ^ V2 ^ ^ V1 1 2 例 V2 V5 ^ V4 ^ V3 ^ V1 1 2 3 4 3 G1 4 5 G2
图的存储结构--数组(邻接矩阵)表示 例 1 2 3 4 5 G2 邻接矩阵 设G=(V,E)是有n1个顶点的图,G的邻接矩阵A是具有以下性质的n阶方阵
图的存储结构--数组(邻接矩阵)表示 例 5 例 1 2 3 8 7 4 5 1 6 3 4 2 1 2 3 4 G1
图的存储结构--数组(邻接矩阵)表示 #define INFINITY INT_MAX #define MAX_VERTEX_NUM 20 typedefenum{DG,DN,UDG,UDN} GraphKind; typedefstruct ArcCell{ //描述弧的信息 VRType adj; //顶点关系,无权图用1和0表示 InfoType *info; }ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM ] typedef struct{ VertexType vexs[MAX_VERTEX_NUM]; AdjMatrix arcs; //邻接矩阵 int vexnum, arcnum; GraphKind kind; }Mgraph;
图的存储结构--数组(邻接矩阵)表示 例: 无向图G1的数组表示存储结构为: Mgraph G1; G1. vexs=[A,B,C,D,E,F] G1.arcs= G1.vexnum=6 G1.arcnum=7 G1.kind=AD
图的邻接矩阵特点: • 无向图的邻接矩阵对称,可压缩存储;有n个顶点的无向图需存储空间为n(n+1)/2 • 有向图邻接矩阵不一定对称;有n个顶点的有向图需存储空间为n² • 无向图中顶点Vi的度TD(Vi)是邻接矩阵A中第i行元素之和 • 有向图中, • 顶点Vi的出度是A中第i行元素之和 • 顶点Vi的入度是A中第i列元素之和
图的存储结构--邻接表(链式存储结构) data firstarc adjvex next 1 2 例 3 a 4 b c d 2 4 3 1 a b c d G1 实现:为图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(有向图中指以Vi为尾的弧) ^ ^ ^ ^
图的存储结构--邻接表(链式存储结构) 1 2 2 4 4 5 3 2 3 3 5 1 例 data firstarc adjvex next a b 1 a ^ c ^ b 2 3 c ^ d e G2 4 d ^ 5 e ^
图的存储结构--邻接表(链式存储结构) adjvex nextarc info 表结点 头结点 Vexdata firstarc typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; Infotype *info }ArcNode; typedef struct Vnode { VertexType vexdata; Arcnode *firstarc; }Vnode,AdjList[MAX_VERTEX_NUM]; typedef struct{ AdjList vertices; int vexnum,arcnum; int kind; }ALGraph;
例 a b c d 4 3 1 1 a b data adjvex firstarc next 1 ^ c d 2 ^ G1 3 ^ 4 ^ • 图的邻接表特点 • 无向图中顶点Vi的度为第i个单链表中的结点数 • 有向图中 • 顶点Vi的出度为第i个单链表中的结点个数 • 顶点Vi的入度为整个单链表中邻接点域值是i的结点个数 • 逆邻接表:有向图中对每个结点建立以Vi为头的弧的单链表
图的存储结构--有向图的十字链表 例 弧结点 顶点结点 1 1 2 1 3 a tailvex headvex hlink tlink info 2 b a b 3 c c d 3 1 3 4 4 d 4 3 4 1 4 2 data firstin firstout ^ ^ ^ ^ ^ ^ ^ ^
图的存储结构--无向图的邻接多重表 边结点 顶点结点 Mark ivex ilink jvex jlink info 5 2 3 5 3 2 3 4 1 4 1 2 1 a 例 b 2 a b 3 c c 4 d d e data firstedge 5 e ^ ^ ^ ^ ^
7.3 图的遍历 图的“遍历“:对图中的每个顶点都进行一次访问且仅进行一次访问。 图的遍历除要确定一条搜索路径之外,还要解决两个问题: (1)如何确保每个顶点都被访问到; (2)如何确保每个顶点只被访问一次。
图的遍历-深度优先搜索 • 深度优先遍历(DFS) __类似于树的先序遍历 • 方法: 1、从图的某一顶点V0出发,访问此顶点; 2、然后依次从V0的未被访问的邻接点出发,深度优先遍历图,直至图中所有和V0相通的顶点都被访问到; 3、若此时图中尚有顶点未被访问,则另选图中一个未被访问的顶点作起点,重复上述过程,直至图中所有顶点都被访问为止 • 每个顶点设一个访问标志,以免被多次访问
图的遍历-深度优先搜索 V1 例 V2 V3 V4 V5 V6 V7 V8 V1 例 V3 V2 V5 V6 V4 V8 V7 深度遍历:V1 V2 V4 V8 V5 V6 V3 V7 深度遍历:V1 V2 V4 V8 V5 V6 V3 V7
图的遍历-深度优先搜索 V1 例 V2 V3 V4 V5 V6 V7 V8 深度遍历:V1 V2 V4 V8 V3 V6 V7 V5
开始 访问Vi,置标志 开始 求Vi邻接点 标志数组初始化 有邻接点w N Vi=1 Y 结束 Vi访问过 Y W访问过 N DFS N Y 求下一邻接点 wVi Vi=Vi+1 Vi==Vexnums DFS N Y 结束 深度优先遍历算法
深度优先遍历算法(伪码表示) void DFS(Graph G, int v) {visited[v] = TRUE; VisitFunc(v); for ( w=FirstAdjVex(G, v); w!=0; w=NextAdjVex(G, v, w) ) if (!visited[w]) DFS(G, w); } 注1:算法中函数 FirstAdjVex 和 NextAdjVex 的具体实现取决于图的存储结构。 注2:对于非连通图,如何确保每个顶点都能被访问到?
以邻接表表示的深度优先遍历 data firstarc adjvex nextarc 1 1 ^ ^ 2 2 3 3 ^ 7 8 3 6 1 5 3 7 2 8 2 3 6 5 4 1 4 2 4 4 ^ V1 例 5 5 ^ V2 V3 6 6 ^ 7 7 V4 V5 V6 V7 ^ 8 8 ^ V8 深度遍历:V1 V3 V7 V6 V2 V5 V8 V4
以邻接表表示的深度优先遍历 V1 例 V2 V3 V4 V5 V6 V7 vexdata V8 firstarc adjvex next 1 1 ^ 2 2 ^ 6 8 3 8 4 2 7 7 2 3 3 ^ 4 4 ^ 5 5 ^ 6 6 ^ 7 7 ^ 8 8 ^ 深度遍历:V1 V3 V7 V6 V2 V4 V8 V5
图的遍历-广度优先搜索 广度优先遍历(BFS)——类似树的层次遍历 • 方法: 1、从图的某一顶点出发,访问此顶点; 2、依次访问V0的各个未曾访问过的邻接点; 3、然后分别从这些邻接点出发,广度优先遍历图,直至图中所有已被访问的顶点的邻接点都被访问到; 4、若此时图中尚有顶点未被访问,则另选图中一个未被访问的顶点作起点,重复上述过程,直至图中所有顶点都被访问为止
图的遍历-广度优先搜索 V1 例 V2 V3 V4 V5 V6 V7 V8 V1 例 V3 V2 V5 V6 V4 V8 V7 广度遍历:V1 V2 V3 V4 V5 V6 V7 V8 广度遍历:V1 V2 V3 V4 V5 V6 V7 V8
图的遍历-广度优先搜索 V1 例 V2 V3 V4 V5 V6 V7 V8 广度遍历:V1 V2 V3 V4 V6 V7 V8 V5
广度优先遍历算法 开始 标志数组初始化 Vi=1 Vi访问过 N BFS Y Vi=Vi+1 Vi==Vexnums N Y 结束
广度优先遍历算法 开始 BFS 访问V0,置标志 初始化队列 V0入队 Y 队列空吗 a N 访问w,置标志 队头V出队 结束 求V邻接点w w入队 N w存在吗 V下一邻接点w Y Y w访问过 N a
广度优先遍历 例 1 2 3 4 5 f f f data firstarc 5 5 4 3 1 2 2 1 4 3 5 1 adjvex nextarc ^ 1 1 1 4 2 2 4 3 ^ 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 3 3 ^ r r r 4 4 ^ 遍历序列:1 遍历序列:1 4 5 遍历序列:1 4 3 5 ^
广度优先遍历 例 1 4 3 2 3 2 2 3 4 5 遍历序列:1 4 3 2 遍历序列:1 4 3 2 f f f vexdata 5 4 3 1 1 5 4 3 2 2 5 1 firstarc adjvex next ^ 1 1 2 2 3 2 5 ^ 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 3 3 ^ r r r 4 4 ^ 遍历序列:1 4 3 2 5 5 5 ^
广度优先遍历 例 1 2 3 4 5 f f f vexdata firstarc 5 4 1 1 5 4 2 3 3 2 5 1 adjvex next ^ 1 1 2 2 ^ 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 5 2 5 3 3 ^ r r r 4 4 ^ 5 5 ^ 遍历序列:1 4 3 2 5 遍历序列:1 4 3 2 5 遍历序列:1 4 3 2 5
广度优先遍历 vexdata firstarc adjvex next 1 1 ^ 2 2 ^ 2 8 3 6 7 8 2 4 7 3 3 ^ 4 4 ^ 5 5 ^ 6 6 ^ 7 7 ^ 8 8 ^
7.4 图的连通性问题 V1 V1 V2 V3 V2 V3 V4 V5 V6 V7 V4 V5 V6 V7 V1 V8 V1 V8 V2 V3 V2 V3 V4 V6 V4 V5 V6 V7 V1 例 V8 V7 V2 V3 V8 V5 V4 V5 V6 V7 V8 深度遍历:V1 V2 V4 V8 V5 V3 V6 V7 广度遍历:V1 V2 V3 V4 V5 V6 V7 V8 广度优先生成树 深度优先生成树
例 A B C D E F G H D A I K L C F E J M L M B J G K I H 深度优先生成森林
生成树 • 深度优先生成树与广度优先生成树 • 生成森林:非连通图每个连通分量的生成树一起组成非连通图的生成森林。 • 说明 • 一个图可以有许多棵不同的生成树 • 所有生成树具有以下共同特点: • 生成树的顶点个数与图的顶点个数相同 • 生成树是图的极小连通子图 • 一个有n个顶点的连通图的生成树有n-1条边 • 生成树中任意两个顶点间的路径是唯一的 • 在生成树中再加一条边必然形成回路 • 含n个顶点n-1条边的图不一定是生成树
7.4.3 最小生成树 7 1 2 7 5 9 13 24 6 5 10 17 12 3 4 18 • 问题提出 要在n个城市间建立通信联络网, 顶点——表示城市 权——城市间建立通信线路所需花费代价 希望找到一棵生成树,它的每条边上的权值之和(即建立 该通信网所需花费的总代价)最小———最小代价生成树 • 问题分析 n个城市间,最多可设置n(n-1)/2条线路 n个城市间建立通信网,只需n-1条线路 问题转化为:如何在可能的线路中选择n-1条,能把所有城市(顶点)均连起来,且总耗费(各边权值之和)最小
最小生成树 • 构造最小生成树方法 • 方法一:普里姆(Prim)算法 • 算法思想:设N=(V,{E})是连通网,TE是N上最小生成树中边的集合 • 初始令U={u0},(u0V), TE= • 在所有uU,vV-U的边(u,v)E中,找一条代价最小的边(u0,v0) • 将(u0,v0)并入集合TE,同时v0并入U • 重复上述操作直至U=V为止,则T=(V,{TE})为N的最小生成树
最小生成树 1 1 例 5 6 1 1 5 2 4 5 5 3 3 3 3 2 4 6 6 5 6 1 1 1 1 1 1 2 4 4 1 1 2 4 3 5 3 3 2 2 3 4 2 4 4 4 5 6 6 6 6 1