1 / 100

第7章 图

第7章 图. 7.1 图的定义和术语 7.2 图的存储结构 7.3 图的遍历 7.4 最小生成树 7.5 有向无环图及应用 7.6 最短路径 习题. 7.1 图的定义和术语. 一、图的定义 图 (Graph) :是一种多对多的结构关系,每个元素可以有零个或多个直接前趋;零个或多个直接后继; 图 G 由两个集合构成,记作: G=(V,E) 其中: V: 顶点的非空有限集合, E: 边的有限集合,边是顶点的无序对或有序对集合。 网 :带权的图。 无向图 :在图 G 中,若所有边是无向边。

Télécharger la présentation

第7章 图

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第7章 图 7.1 图的定义和术语 7.2 图的存储结构 7.3 图的遍历 7.4 最小生成树 7.5 有向无环图及应用 7.6 最短路径 习题

  2. 7.1 图的定义和术语 一、图的定义 • 图(Graph):是一种多对多的结构关系,每个元素可以有零个或多个直接前趋;零个或多个直接后继; • 图G由两个集合构成,记作: G=(V,E) 其中: • V:顶点的非空有限集合, • E:边的有限集合,边是顶点的无序对或有序对集合。 • 网:带权的图。 • 无向图:在图G中,若所有边是无向边。 • 有向图:在图G中,若所有边是有向边。 • 混合图:在图G中,既有无向边也有有向边。

  3. V4 V2 V3 V1 V5 • 例1: G1=(V1,E1) V1={v1,v2,v3,v4 ,v5 } E1={(v1,v2),(v1,v4),(v2,v3),(v2,v5),(v3,v4),(v3,v5)} 顶点 边 G1图示

  4. V1 V2 V3 V4 • 例2: G2=(V2,E2) V2={v1,v2,v3,v4 } E2={<v1,v2>, <v1,v3>, <v3,v4> , <v4,v1>} 顶点 弧 弧尾(初始点) 弧头(终端点) G2图示

  5. 抽象数据类型图的定义 • ADT Graph { • 数据对象V: V是具有相同特性的数据元素的集合,称为顶点集。 • 数据关系R: R={VR} VR= {<v,w>| v,w∈V且P(v,w), <v,w>表示从v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息 } • 基本操作P: …… }ADT Graph

  6. 基本操作 • 结构的建立和销毁: • CreateGraph(&G,V,VR); • 按V和VR的定义构造图G。 • DestroyGraph(&G); • 销毁图G。 • 对顶点的访问操作: • LocateVex(G, u); • 若G中存在顶点u,则返回该顶点在图中位置;否则返回其它信息。 • GetVex(G, v); • 返回v的值。 • PutVex(&G, v, value); • 对v赋值value。

  7. 对邻接点的操作: • FirstAdjVex(G, v); • 返回v的第一个邻接点。若该顶点在G中没有邻接点,则返回“空”。 • NextAdjVex(G, v, w); • 返回v的(相对于w的)下一个邻接点。若w是v的最后一个邻接点,则返回“空” • 插入或删除顶点 • InsertVex(&G, v); • 在图G中增添新顶点v。 • DeleteVex(&G, v); • 删除G中顶点v及其相关的弧。

  8. 插入和删除弧 • InsertArc(&G, v, w); • 在G中增添弧<v,w>,若G是无向的,则还增添对称弧<w,v>。 • DeleteArc(&G, v, w); • 在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v>。 • 遍历 • DFSTraverse(G, v, Visit()); • 从顶点v起深度优先遍历图G,并对每个顶点调用函数Visit一次且仅一次。 • BFSTraverse(G, v, Visit()); • 从顶点v起广度优先遍历图G,并对每个顶点调用函数Visit一次且仅一次。

  9. V4 V2 V3 V1 V5 二、图的应用举例 • 例1. 交通图(公路、铁路) • 顶点:地点 • 边:连接地点的公路 • 交通图中的有单行道双行道,分别用有向边、无向边表示; • 例2. 电路图 • 顶点:元件 • 边:连接元件之间的线路

  10. 例3. 通讯线路图 • 顶点:地点 • 边:地点间的连线 • 例4. 各种流程图(如:产品的生产流程图) • 顶点:工序 • 边:各道工序之间的顺序关系

  11. V4 V2 V3 V1 V5 • 三、图的基本术语 • 完全图:图G任意两个顶点都有一条边相连接 • 若 n 个顶点的无向图有 n(n-1)/2 条边, 称为无向完全图 • 若 n 个顶点的有向图有n(n-1)条边, 称为有向完全图 • 稀疏图:边较少的图。通常e<<nlogn • 稠密图:边很多的图。 e1

  12. V4 V2 V1 V3 V2 V3 V4 V1 V5 • 邻接(adjacent)点:边的两个顶点 • 关联(incident)边:若边e= (v, u), 则称顶点v、u 关联边e • 顶点的度:和某顶点相连的邻接点数 • 顶点的入度:指向某顶点的弧的数目 • 顶点的出度:从某顶点出发的弧的数目 • 设图G的顶点数为n,边数为e • 图的所有顶点的度数和 = 2*e • 每条边对图的所有顶点的度数和“贡献”2度

  13. V4 V2 V3 V1 V2 V3 V4 V1 V5 • 路径:两个顶点之间的顶点序列。 • 该序列的每个顶点与其前驱是邻接点,每个顶点与其后继也是邻接点 • 回路(环):第一顶点和最后顶点相同的路径。 • 图1:V1,V2,V3,V4 是V1到V4的路径;V1,V2,V3,V4,V1是回路; • 图2:V1,V3,V4 是V1到V4的路径; V1,V3,V4,V1是回路。 • 简单路径: 顶点不重复的路径。 图1 图2

  14. V4 V2 V2 V5 V6 V1 V3 V4 V1 V5 V3 • 连通: 两个顶点之间有路径 • 连通图: 任意两个顶点之间有路径(无向图) • 强连通图:任意两个顶点之间有双向路径(有向图) 连通图 非连通图

  15. V4 V4 V2 V2 V2 V3 V3 V3 V1 V1 V1 V5 V5 V5 • 子图:设有两个图G=(V,E)、G1=(V1,E1),若V1 V,E1  E,则称 G1是G的子图; • 例:图2、图3 是 图1 的子图 图2 图3 图1

  16. V2 V5 V6 V1 V4 连通分图 V3 • 连通分图: 无向图中的极大连通子图。 • 强连通分量:有向图中的极大强连通子图。 • 极大(强)连通子图:该子图是图G的(强)连通子图,将G的任何不在该子图中的顶点加入,子图不再(强)连通。

  17. V4 V4 V2 V2 V3 V3 V1 V1 V5 V5 • 生成树:包含无向图G所有顶点的的极小连通子图。 • 极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边,子图不再连通 • 若T是G的生成树当且仅当T满足如下条件: • T是G的连通子图 • T包含G的所有顶点 • T中无回路

  18. 7.2 图的存储结构 • 图的存储结构至少要保存两类信息: • 1)顶点的数据 • 2)顶点间的关系 • 顶点的编号 • 为了使图的存储结构与图一一对应,在讨论图的存储结构时,首先要给图的所有顶点编号。 • 设 G=<V, E>是图, V={v1,v2,v3, …vn },设顶点的的下标为它的编号。 • 本课程介绍两类图的存储结构 • 数组表示法 • 邻接表(邻接表,逆邻接表)

  19. V4 V2 V3 V1 V2 V3 V4 • 0 1 0 1 0 • 0 1 0 1 • 0 1 0 1 1 • 0 1 0 0 • 0 1 1 0 0 • 0 1 1 0 • 0 0 0 0 • 0 0 0 1 • 0 0 0 V1 V5 一、数组表示法 • 用邻接矩阵表示顶点间的关系。 • 邻接矩阵:G的邻接矩阵是满足如下条件的n阶矩阵: 1 若(vi,vi+1)E 或 <vi,vi+1>E 0 否则 A[i][j]=

  20. Wij <vi, vj> 或(vi, vj)∈VR A [ i ][ j ]= ∞ 无边(弧) 5 v1 v2 4 8 3 N 7 9 v3 v6 6 1 5 v4 v5 5 • 网(即有权图)的邻接矩阵 ∞5∞ 7 ∞∞ ∞∞4∞∞∞ 8∞∞∞ ∞9 ∞∞5 ∞∞6 ∞∞∞ 5 ∞∞ 3∞∞ ∞ 1∞

  21. 0 1 0 1 0 1 0 1 0 1 0 1 2 3 4 m m+1 m+2 m+3 m+4 V1 V2 V3 V4 V5 0 1 2 3 4 5 m-1 顶点的存储:用一维数组存储(按编号顺序); 顶点间关系:用二维数组存储图的邻接矩阵。 存储邻接矩阵的二维数组 存储顶点的一维数组

  22. 数组表示法类型定义 #define MAX_VERTEX_NUM m //最大顶点个数 typedef enum {DG,DN,UDG,UDN} GraphKind; //{有向图,有向网,无向图,无向网} typedef struct ArcCell { VRType adj; //VRType 是顶点关系类型。 //无权图:用1或0表示相邻否;对带权图:则为权值类型 }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; typedef struct { VertexType vexs[MAX_VERTEX_NUM]; //存储顶点 AdjMatrix arcs; //存储邻接矩阵 int vexnum, arcnum; //图的当前顶点数和弧数 GraphKind kind; //图的种类标志 }Mgraph;

  23. 顶点数组 V1 0 n e UDG G.vexs G.arcs G.vexnumG.arcnu G.kind 存储邻接矩阵的二维数组 • 设G是Mgraph 类型的变量,用于存储无向图,该图有n个顶点,e条边。 • G的图示如下:

  24. 无向图数组表示法特点: • 1)无向图邻接矩阵是对称矩阵,同一条边表示了两次; • 2)顶点v的度:二维数组对应行(或列)中1的个数; • 3)两顶点是否为邻接点:只需判二维数组对应分量是否为1; • 4)增加、删除边:只需对二维数组对应分量赋值1或清0; • 5)设存储顶点的一维数组大小为n, G占用存储空间:n+n2。G占用存储空间只与它的顶点数有关,与边数无关。 • 对有向图的数组表示法可做类似的讨论。

  25. 用邻接矩阵生成无向网的算法 Status CreateUDN(Mgraph &G) { //无向网的构造,用邻接矩阵表示 scanf(&G.vexnum, &G.arcnum, &IncInfo); for(i=0;i<G.vexnum,;++i) scanf(&G.vexs[i] ); //构造顶点向量 for(i=0; i<G.vexnum; ++i) //初始化邻接矩阵 for(j=0;j<G.vexnum;++j) G.arcs[i][j]={INFINITY, NULL}; for(k=0;k<G.arcnum;++k){ //构造邻接矩阵 scanf(&v1, &v2, &w); //输入弧的两顶点以及对应权值 i=LocateVex(G,v1); j=LocateVex(G,v2); //找到顶点在矩阵中的位置 G.arcs[i][j].adj=w; //输入对应权值 if(IncInfo) Input(*G.arcs[i][j].info); //如果弧有信息则填入 G.arcs[i][j] = G.arcs [j] [i]; //无向网是对称矩阵 } return OK; } //CreateUDN 对于n个顶点e条弧的网, 时间复杂度= O(n2+n+e*n)

  26. 0 1 2 3 4 m-1 V1 V2 V3 V4 V5 4 0 V4 V2 V3 1 3 0 1 3 2 V1 1 4 2 V5 二、邻接表 1. 无向图的邻接表 • 顶点:按编号顺序将顶点数据存储在一维数组中; • 关联同一顶点的边:用线性链表存储。 • 例 2

  27. 图的邻接表类型定义 #define MAX_VERTEX_NUM 20 typedef struct ArcNode{ //弧的类型定义 int adjvex; //弧指向的顶点的位置 struct ArcNode *nextarc; //指向下一条弧的指针 InfoType *info; }ArcNode; typedef struct Vnode { //顶点的类型定义 VertexType data; //顶点信息 ArcNode * firstarc; //指向关联该顶点的弧链表 }Vnode, AjList[MAX_VERTEX_NUM]; typedef struct { //图的类型定义 AdjList vertices; int vexnum, arcnum; //图的顶点数和弧数 int kind; //图的种类标志 }ALGraph;

  28. 0 1 2 4 m-1 1 2 V4 V2 V3 G.vertices G.vexnum G.arcnu G.kind V1 V2 V3 V4 V5 3 1 4 4 3 0 V1 2 0 1 V5 5 6 UDG • 设G是ALGraph 类型的变量,用于存储无向图G1,该图有n个顶点,e条边 • G的图示如下: data firstarc adjvex nextarc 2 无向图G1

  29. 无向图的邻接表的特点 • 1)在G邻接表中,同一条边对应两个结点; • 2)顶点v的度:等于v对应线性链表的长度; • 3)两顶点是否邻接:要看v对应线性链表中有无对应的结点u; • 4)增减边:要在两个单链表插入、删除结点; • 5)设存储顶点的一维数组大小为n, 图的边数为e,G占用存储空间为:n+2*e。G占用存储空间与G的顶点数、边数均有关。

  30. D.vertices D.vexnum D.arcnu D.kind V1 V2 V3 V4 V1 V2 V3 V4 0 1 2 4 4 DG 3 2. 有向图的邻接表和逆邻接表 • 1)有向图的邻接表 • 顶点:用一维数组存储(按编号顺序) • 以同一顶点为起点的弧:用线性链表存储 顶点表 出边表 0 1 2 3 m-1

  31. D.vertices D.vexnum D.arcnu D.kind V1 V2 V3 V4 V1 V2 V3 V4 3 0 0 2 4 4 DG • 2)有向图的逆邻接表 • 顶点:用一维数组存储(按编号顺序) • 以同一顶点为终点的弧:用线性链表存储表 顶点表 入边表 0 1 2 3 m-1

  32. D.vertices D.vexnum D.arcnu D.kind A B C D 4 4 DG • 网络 (带权图) 的邻接表 1 5 1 9 3 2 2 8 顶点表 出边表 3 6 0 1 2 3 m-1 6 A D 9 5 2 B C 8

  33. 比较:邻接矩阵和邻接表

  34. 三、十字链表(自学) • 它是有向图的另一种链式结构。 • 思路:邻接表、逆邻接表的结合。 • 1、每条弧对应一个结点(称为弧结点) • 2、每个顶点也对应一个结点(称为顶点结点)

  35. 弧结点 tailvex: 弧尾顶点位置 headvex: 弧头顶点位置 hlink: 弧头相同的下一弧位置 tlink: 弧尾相同的下一弧位置 info: 弧信息 顶点结点 data : 存储顶点信息。 Firstin : 以顶点为弧头的第一条弧结点。 Firstout: 以顶点为弧尾的第一条弧结点。

  36. 十字链表存储结构: typedef struct ArcBox { //弧结点结构 int tailvex , headvex ; struct ArcBox * hlink , *tlink; InfoType *info; } ArcBox; Typedef struct VexNode{ //顶点结构 VertexType data; ArcBox * firstin,*firstout; } VexNode; #define MAX_VERTEX_NUM 20 Typedef struct { VexNode xlist[MAX_VERTEX_NUM ]; //表头向量 int vexnum, arcnum; } OLGraph;

  37. 例:画出有向图的十字链表。 v2 v1 v3 v4 0 v1 0 1 0 2 ^ 1 v2 3 2 2 3 ^ ^ ^ ^ 2 v3 2 0 3 v4 3 0 ^ 3 1 ^ ^ 数组下标 tlink tailvex hlink headvex ^ firstin firstout 十字链表优点:容易操作,如求顶点的入度、出度等。 空间复杂度与邻接表相同;建立的时间复杂度与邻接表相同。

  38. 四、邻接多重表(自学) • 无向图的另一种存储结构 • 需要对边操作时,无向图常采用此种结构存储。 • 1.每条边只对应一个结点(边结点) • 比较:邻接表中同一条边用两个结点表示 • 2.每个顶点也对应一个结点(顶点结点)

  39. 边结点 mark:标志域,如处理过或搜索过。 ivex, jvex : 顶点域,边依附的两个顶点位置。 ilink: 指向下一条依附顶点 i 的边结点位置。 jlink; 指向下一条依附顶点 j 的边结点位置。 info: 边信息,如权值等。 顶点结点 data : 存储顶点信息。 Firstedge : 依附顶点的第一条边结点。

  40. 无向图的邻接多重表存储表示 typedef struct Ebox { VisitIf mark; //访问标记 int ivex, jvex; //该边依附的两个顶点的位置 struct EBox *ilink, *jlink; InfoType *info; //该边信息指针 } Ebox; typedef struct VexBox { VertexType data; EBox *firstedge; // 指向第一条依附该顶点的边 } VexBox; typedef struct { // 邻接多重表 VexBox adjmulist[MAX_VERTEX_NUM]; int vexnum, edgenum; } AMLGraph;

  41. 例:画出无向图的邻接多重表 v2 v1 v3 v4 v4 v5 0 1 0 ^… 3 0 v1 1 2 1 ^ 4 1 v2 2 v3 2 3 2 ^ 4 3 v4 3 ^ 4 ^ 4 v5 数组下标不属结点分量 ivex jvex jlink ilink 邻接多重表优点: 容易操作,如求顶点的度等。 空间复杂度与邻接表相同; 建立的时间复杂度与邻接表相同。 firstedge

  42. 7.3 图的遍历 • 图的遍历:从图的某顶点出发,访问图中所有顶点,并且每个顶点仅访问一次。 • 图中可能有回路,遍历可能沿回路又回到已遍历过的结点。为避免同一顶点被多次访问,必须为每个被访问的顶点作一标志。 • 为此引入一辅助数组, 记录每个顶点是否被访问过。 • 有两种遍历方法: • 深度优先遍历 ( DFS, Depth First Search ) • 广度优先遍历 ( BFS , Breadth First Search )

  43. 0 1 2 3 4 5 6 7 visited 1 1 0 1 1 0 0 1 V3 V5 V8 V2 V1 V6 V4 V7 7.3.1 深度优先遍历(类似于树的先根遍历) 从图中某顶点v出发: • 1)访问顶点v; • 2)从v的未被访问的邻接点出发,继续对图进行深度优先遍历; • 例:图G中,以V1为起点的深度优先序列: • V1,V2,V4,V8,V5,V3,V6,V7 注:为简单起见,只讨论非空连通图的遍历

  44. visited 0 0 0 0 0 0 1 2 3 4 m-1 Boolean visited[MAX_VERTEX_NUM] //访问标志数组, visited[v]=TRUE表示顶点v已被访问 深度优先遍历算法 void DFS(Graph G, int v) { // 从第v个顶点出发,递归地深度优先遍历连通图G。 // v是顶点在一维数组中的位置,假设G是非空图 visited[v] =TRUE; Visit(v); //访问第v个顶点 for (w= FirstAdjVex(G, v); w>=0; w=NextAdjVex(G, v, w)) if (!visited[w]) DFS(G, w); //对v的尚未访问的邻接顶点w递归调用DFS }

  45. DFS 算法效率分析 设图中有 n 个顶点,e 条边 • 遍历图的过程实质上是对每个顶点查其找邻接点 • 邻接矩阵:查找每个顶点的邻接点所需的时间为O(n2)。 • 邻接表:查找邻接点需扫描 e 个结点,加上访问 n个头结点的时间,因此遍历图的时间复杂度为O(n+e)。 • 结论: • 稠密图适于在邻接矩阵上进行深度遍历; • 稀疏图适于在邻接表上进行深度遍历。

  46. visited 1 0 0 0 0 0 0 0 V3 V5 V8 V2 V1 V6 V4 V7 7.3.2 广度优先遍历(类似于树的按层遍历) 从图中某顶点v出发: • 1)访问顶点v ; • 2)访问v的所有未被访问的邻接点w1 ,w2 ,…wk ; • 3)依次从这些邻接点出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问; • 例:图G中,以V1起点的的广度优先序列: • V1,V2,V3,V4,V5,V6,V7,V8 0 1 2 3 4 5 6 7

  47. 为实现 3) • 需要保存在步骤2)中访问的顶点; • 访问这些顶点邻接点的顺序为: • 先保存的顶点,其邻接点先被访问。 需设置一队列Q,保存已访问的顶点,并控制遍历顶点的顺序。

  48. void BFSTraverse(Graph G,int v,Status (* Visit)(int v)) { //从v出发,广度优先非递归遍历连通图G。 //使用辅助队列Q和访问标志数组visited。 for (u=0; u< G.vexnum; ++u) visited[u]=FALSE; InitQueue(Q); //建空的辅助队列Q visited[v]=TRUE; Visit(v);EnQueue(Q,v) //访问v,v入队 while(!QueueEmpty(Q)){ DeQueue(Q,u); //队头元素出队,并赋值给u //访问u所有未被访问的邻接点 for(w=FirstAdjVex(G,u); w>=0; w=NextAdjVex(G,u,w)) if(!visited[w]){ // 若w尚未访问 visited[w]=TRUE; Visit(w);EnQueue(Q,w); }//if }//while }//BFSTraverse

  49. BFS 算法效率分析: • 设图中有 n 个顶点,e 条边 • 邻接矩阵:对于每一个被访问到的顶点,都要循环检测矩阵中的整整一行(n 个元素),总的时间代价为O(n2)。 • 邻接表:总时间代价为d0 + d1 + … + dn-1 = O(e),其中的di 是顶点i 的度。 • DFS与BFS之比较: • 空间复杂度:相同,O(n)(借用了堆栈或队列); • 时间复杂度:只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关。

  50. 无向图的连通性 • 对于连通图,从图中任一顶点出发, 利用深度优先搜索算法或广度优先搜索算法,便可访问到到图中的所有顶点 • 深度优先搜索得到深度优先生成树 • 广度优先搜索得到广度优先生成树 • 对于非连通图, 则需从每一个连通分量中的任一个顶点出发进行遍历, 可求得无向图的所有连通分量 • 所有连通分量的生成树组成了非连通图的生成森林。

More Related