1 / 61

第 10 章 图

第 10 章 图. 本章前面介绍了线性表、树,本章将介绍另一种重要的数据结构 -- 图结构。图结构与表结构和树结构的不同表现在结点之间的关系上,线性表中结点之间的关系是一对一的,除头结点和尾结点外,每个结点仅有一个前驱和一个后继;树是按分层关系组织的结构,树结构中结点之间关系是一对多,一个双亲可以有多个孩子,每个孩子结点仅有一个双亲;而图结构中,顶点之间的关联是任意的,图中每个顶点都可以和其它顶点相关联,即数据元素之间存在多对多的关系。相应地,图的存储及其运算也较为复杂。本章将介绍图的概念,如何在计算机上表示和处理图,以及如何利用图来解决一些实际问题。. 图的应用举例

Download Presentation

第 10 章 图

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. 第10章 图

  2. 本章前面介绍了线性表、树,本章将介绍另一种重要的数据结构--图结构。图结构与表结构和树结构的不同表现在结点之间的关系上,线性表中结点之间的关系是一对一的,除头结点和尾结点外,每个结点仅有一个前驱和一个后继;树是按分层关系组织的结构,树结构中结点之间关系是一对多,一个双亲可以有多个孩子,每个孩子结点仅有一个双亲;而图结构中,顶点之间的关联是任意的,图中每个顶点都可以和其它顶点相关联,即数据元素之间存在多对多的关系。相应地,图的存储及其运算也较为复杂。本章将介绍图的概念,如何在计算机上表示和处理图,以及如何利用图来解决一些实际问题。本章前面介绍了线性表、树,本章将介绍另一种重要的数据结构--图结构。图结构与表结构和树结构的不同表现在结点之间的关系上,线性表中结点之间的关系是一对一的,除头结点和尾结点外,每个结点仅有一个前驱和一个后继;树是按分层关系组织的结构,树结构中结点之间关系是一对多,一个双亲可以有多个孩子,每个孩子结点仅有一个双亲;而图结构中,顶点之间的关联是任意的,图中每个顶点都可以和其它顶点相关联,即数据元素之间存在多对多的关系。相应地,图的存储及其运算也较为复杂。本章将介绍图的概念,如何在计算机上表示和处理图,以及如何利用图来解决一些实际问题。

  3. 图的应用举例 例1、交通图(公路、铁路) 顶点:地点 边:连接地点的路 例2、电路图 顶点:元件 边:连接元件之间的线路 例3、通讯线路图 顶点:地点 边:地点间的连线 例4、各种流程图 如产品的生产流程图。 顶点:工序 边:各道工序之间的顺序关系

  4. 图的基本概念 • 图的基本概念 • 图的遍历 • 图的连通性与最小生成树

  5. 图的基本概念 • 图(Graph)——图G是由两个集合V(G)和E(G)组成的,记为G=(V,E) 其中:V(G)是顶点的非空有限集 E(G)是边的有限集合,边是顶点的无序对或有序对。 • 图的分类 • 有向图 • 无向图

  6. 图的基本概念 • 有向图——有向图G是由两个集合V(G)和E(G)组成的。 其中:V(G)是顶点的非空有限集。 E(G)是有向边(也称弧)的有限集合,弧是顶点的有序对,记为<v,w>,v,w是顶点,v为弧尾,w为弧头。

  7. 图的基本概念 • 例如: G1 = <V2,E2> V1 = { A, B, C, D, E } E1 = {<A,B>, <A,E>, <B,C>, <C,D>, <D,B>, <D,A>, <E,C> } A B E C D

  8. 图的基本概念 • 无向图——无向图G是由两个集合V(G)和E(G)组成的。 其中:V(G)是顶点的非空有限集。 E(G)是边的有限集合,边是顶点的无序对,记为(v,w) 或(w,v),并且(v,w)=(w,v)。

  9. 图的基本概念 • 例如: G2 = <V1,E1> V2 = { v0 ,v1,v2,v3,v4 } E2 = { (v0,v1), (v0,v3), (v1,v2), (v1,v4), (v2,v3), (v2,v4) } V1 V0 V2 V3 V4

  10. 图的基本概念 V0 V4 V3 V2 V0 V1 V1 V2 V3 1 邻接点及关联边 邻接点:边的两个顶点 关联边:若边e= (v, u), 则称顶点v、u 关连边e 2 顶点的度、入度、出度顶点V的度= 与V相关联的边的数目 在有向图中: 顶点V的出度= 以V为起点有向边数 顶点V的入度= 以V为终点有向边数 顶点V的度= V的出度+V的入度 设图G 的顶点数为 n,边数为 e 图的所有顶点的度数和 = 2*e (每条边对图的所有顶点的度数和“贡献”2度) e

  11. 图的基本概念 • 路径、回路 无向图 G =(V,E)中的顶点序列v1, v2, … ,vk,若 (vi,vi+1)E ( i=1, 2 ,… k-1), v=v1, u=vk,则称该序列是从顶点v到顶点u的路径;若v=u,则称该序列为回路。 有向图 D =(V,E)中的顶点序列 v1, v2, …, vk,若 <vi,vi+1>E (i=1,2,…k-1),v=v1,u=vk,则称该序列是从顶点v到顶点u的路径;若v=u,则称该序列为回路。

  12. 图的基本概念 V0 V4 V3 V2 V0 V1 V1 V2 V3 • 例如 在图G1中,V0, V1, V2, V3 是 V0 到 V3 的路径;V0, V1, V2, V3, V0 是回路。 在图G2中,V0, V2, V3 是 V0 到 V3 的路径; V0, V2, V3, V0 是回路。 无向图G1 有向图G2

  13. 图的基本概念 V4 V0 V0 V1 V3 V3 V4 V2 V0 V1 V2 V3 V0 V2 V3 V5 V2 V1 V1 • 连通图(强连通图) 在无(有)向图 G=< V, E > 中,若对任何两个顶点 v、u 都存在从 v 到 u 的路径,则称G是连通图(强连通图) 非连通图 连通图 非强连通图 强连通图

  14. 图的基本概念 V4 V3 V3 V4 V0 V0 V4 V3 V0 V2 V2 V2 V1 V1 V1 • 子图 设有两个图 G=(V,E),G1=(V1,E1),若V1 V,E1  E,E1关联的顶点都在 V1 中,则称 G1 是 G 的子图; • 例 (b)、(c)是 (a)的子图 (c) (a) (b)

  15. 图的基本概念 V0 V2 V3 V5 V4 V1 • 连通分量(强连通分量) 无向图G的极大连通子图称为G的连通分量。 极大连通子图意思是:该子图是G连通子图,将G的任何不在该子图中的顶点加入,子图不再连通。 连通分图 非连通图

  16. 图的基本概念 V0 V1 V2 V3 V0 V2 V3 V1 强连通分量 • 连通分量(强连通分量) 有向图D的极大强连通子图称为D的强连通分量。 极大强连通子图意思是:该子图是D强连通子图,将D的任何不在该子图中的顶点加入,子图不再是强连通的。

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

  18. 图的存储结构 1 若(vi,vj)E 或 <vi,vj >E 0 否则 A[i][j]= V0 V1 V2 V3 V0 V4 V3 V2 • 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 一、数组表示法(邻接矩阵表示) 邻接矩阵:G的邻接矩阵是满足如下条件的n阶矩阵: 在数组表示法中,用邻接 矩阵表示顶点间的关系

  19. 图的存储结构 4 4 0 V0 V4 V3 1 3 V0 V1 V2 V3 V4 0 1 2 3 4 m-1 0 V2 1 3 2 2 1 2 V1 二、邻接表 邻接表是图的链式存储结构 1、无向图的邻接表 顶点:通常按编号顺序将顶点数据存储在一维数组中; 关联同一顶点的边:用线性链表存储。 该结点表示边 (V2,V4),其中的4是V4在一 维数组中的位置

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

  21. 图的存储结构 v0 v1 v2 vexdata firstarc v3 0 0 3 1 2 1 2 V0 V1 3 V2 V3 二、邻接表 2、有向图的邻接表 顶点:用一维数组存储(按编号顺序) 以同一顶点为起点的弧:用线性链表存储 adjvex next ^ ^ ^ ^ 类似于无向图的邻接表,所不同的是: 以同一顶点为起点的弧:用线性链表存储

  22. 图的存储结构 v0 v1 v2 vexdata firstarc v3 2 0 0 3 0 1 2 V0 V1 3 V2 V3 二、邻接表 3、有向图的逆邻接表 顶点:用一维数组存储(按编号顺序) 以同一顶点为终点的弧:用线性链表存储。 ^ ^ ^ ^ 类似于有向图的邻接表,所不同的是: 以同一顶点为终点弧:用线性链表存储

  23. 图的存储结构 弧结点: typedef struct ArcBox { int tailvex, headvex; //弧尾、弧头在表头数组中位置 struct arcnode *hlink;//指向弧头相同的下一条弧 struct arcnode *tlink; //指向弧尾相同的下一条弧 } ArcBox; tailvex headvex hlink tlink 顶点结点: typedef struct VexNode { VertexType data; //存与顶点有关信息 ArcBox *firstin;//指向以该顶点为弧头的第一个弧结点 ArcBox *firstout; //指向以该顶点为弧尾的第一个弧结点 } VexNode; VexNode OLGraph[M]; data firstin firstout 三、有向图的十字链表表示法

  24. 图的存储结构 例 1 1 2 1 3 a 2 b a b 3 c c d 3 1 3 4 4 d 4 3 4 1 4 2 三、有向图的十字链表表示法 相同弧尾 相同弧头 ^ ^ ^ ^ ^ ^ ^ ^

  25. 图的存储结构 边结点: typedef struct node { VisitIf mark; //标志域,记录是否已经搜索过 int ivex, jvex; //该边依附的两个顶点在表头数组中位置 struct EBox * ilink, * jlink; //分别指向依附于ivex和jvex的下一条边 }EBox; mark ivex ilink jvex jlink 顶点结点: typedef struct VexBox { VertexType data; //存与顶点有关的信息 EBox * firstedge; //指向第一条依附于该顶点的边 }VexBox; VexBox AMLGraph[M]; data firstedge 四、无向图的邻接多重表表示法

  26. 图的存储结构 1 3 5 3 2 1 4 3 4 5 2 1 2 a 例 b 2 a b 3 c c 4 d d e 5 e 四、无向图的邻接多重表表示法 ^ ^ ^ ^ ^

  27. 小结 本小节介绍了图的基本概念和图的存储结构。这些基本概念包括有向图、无向图、度、入度、出度、子图、路径、连通图与强连通图等。 图的存储结构至少要保存两类信息: 1)顶点的数据 2)顶点间的关系 常用的存储结构包括邻接矩阵、邻接表、邻接多重表和十字链表。 无向图的邻接矩阵是对称的,有向图的邻接矩阵可能是不对称的。 用邻接矩阵表示图,很容易判定任意两个顶点之间是否有边相连,并求得各个顶点的度数。无向图,顶点vi的度数是邻接矩阵中第i行或第i列值为1的元素个数, 有向图,邻接矩阵中第i行值为1的元素个数为顶点vi的出度,第i列值为1的元素的个数为顶点vi的入度, 用邻接矩阵表示法存储图,占用的存储单元只与图中顶点的个数有关,而与边的数目无关。 而用邻接表存储图,占用的存储单元为n+2e 在邻接多重表中, 所有依附于同一个顶点的边都链接在同一个单链表中。 从顶点 i 出发, 可以循链找到所有依附于该顶点的边,也可以找到它的所有邻接顶点。

  28. 图的遍历 V6 V7 V3 V4 V2 V1 V5 V8 V4 V1 V8 V7 V3 V5 V2 V6 • 连通图的深度遍历(DFS) 从图中某顶点v出发: 1)访问顶点v;2)对v的每个未被访问的邻接点wj,从wj出发,继续对图进行深度优先遍历。 例 深度遍历:V1,V2,V4,V5,V8,V3,V6,V7 深度遍历:V1,V3,V6,V7,V2,V5,V8,V4

  29. 图的遍历 • 图的深度遍历(DFS) V 从图解可见: 1. 从深度优先搜索遍历连通图的过程类似于树的先根遍历; w1 w3 w2 w2 SG3 SG1 SG2 2. 如何判别V的邻接点是否被访问? 解决的办法是:为每个顶点设立一个 “访问标志 visited[w]”。

  30. 图的遍历 Boolean visited[MAX]; // 访问标志数组 Status (*VisitFunc)(int v); // 访问函数 void DFS ( Graph G, int v) { // 从顶点v出发,深度优先搜索遍历连通图 G visited[v] = TRUE; VisitFunc(v); for ( w=FirstAdjVex(G, v); w!=0; w=NextAdjVex(G,v,w) ) if ( !visited[w] ) DFS(G, w); // 对v的尚未访问的邻接顶点w // 递归调用DFS } // DFS

  31. 图的遍历 • 非连通图的深度优先搜索遍历 首先将图中每个顶点的访问标志设为 FALSE,之后搜索图中每个顶点,如果未被访问,则以该顶点为起始点,进行深度优先搜索遍历,否则继续检查下一顶点。

  32. 图的遍历 void DFSTraverse ( Graph G, Status (*Visit) (int v)) { // 对图 G 作深度优先遍历。 VisitFunc = Visit; for ( v=0; v<G.vexnum; ++v ) visited[v] = FALSE; // 访问标志数组初始化 for ( v=0; v<G.vexnum; ++v ) if (!visited[v]) DFS(G, v); // 对尚未访问的顶点调用DFS }

  33. 图的遍历 例如: b a b a g g f c d e f c d e k h k h 0 1 2 3 4 5 6 7 8 访问标志: F F F F F F F F F T T T T T T T T T 访问次序: a c h d k f e b g

  34. 图的遍历 • 图的深度遍历(DFS) 开始 开始 访问V,置标志 标志数组初始化 求V邻接点 V=0 V访问过? 有邻接点w N N Y Y DFS(g,v) 结束 Y W访问过 V=V+1 N Y 求下一邻接点 V<VexNum DFS(G,w) N 结束

  35. 图的遍历 例 vexdata firstarc adjvex next V1 1 1 ^ 2 2 ^ V2 V3 3 3 ^ 4 4 ^ V4 V5 V6 V7 7 7 8 3 6 2 2 4 8 5 5 ^ 6 6 ^ V8 7 7 ^ 8 8 ^ • 图的深度遍历(DFS)——递归算法 深度遍历:V1 V3  V7  V6  V2  V4  V8  V5

  36. 图的遍历 • 图的广度遍历(BFS) 从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到。 若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

  37. 图的遍历 V5 V6 V7 V8 V3 V4 V2 V1 V1 V8 V7 V6 V2 V4 V3 V5 • 图的广度遍历(BFS) 从图中某顶点v出发: 1) 访问顶点v 2) 访问v的所有未被访问的邻接点w1,w2,…wk 3) 依次从这些邻接点出发,访问它们的所有未被访问的邻接点,直到图中所有访问过的顶点的邻接点都被访问到。 V1,V2,V3,V4,V8,V5,V6,V7 V1,V3,V2,V6,V7,V4,V5,V8

  38. 图的遍历 void BFSTraverse ( Graph G, Status (*Visit) (int v) ) { for ( v=0; v<G.vexnum; ++v ) visited[v] = FALSE; // 初始化访问标志 InitQueue(Q); // 置空的辅助队列Q for ( v=0; v<G.vexnum; ++v ) if ( !visited[v]) { // v 尚未访问 … //next page } } // BFSTraverse

  39. 图的遍历 visited[v] = TRUE; Visit(v); // 访问v EnQueue(Q, v); // v入队列 while (!QueueEmpty(Q)) { DeQueue(Q, u); // 队头元素出队并置为u for ( w=FirstAdjVex(G, u); w!=0; w=NextAdjVex(G,u,w) ) if ( ! visited[w] ) { visited[w]=TRUE; Visit(w); EnQueue(Q, w); // 访问的顶点w入队列 } // if } // while

  40. 图的遍历 例 1 firstarc adjvex next vexdata ^ 1 1 2 3 4 2 2 ^ 3 3 ^ 5 4 4 ^ f f f 3 5 5 5 4 1 2 2 1 4 3 1 5 5 ^ 1 4 4 3 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 r r r 遍历序列:1 遍历序列:1 4 遍历序列:1 4 3 • 图的广度遍历(BFS)

  41. 图的遍历 例 1 firstarc adjvex next vexdata 4 3 2 3 2 ^ 1 1 2 3 4 2 2 ^ 3 3 ^ 5 遍历序列:1 4 3 2 遍历序列:1 4 3 2 4 4 ^ f f f 5 5 4 2 2 3 5 1 1 4 1 3 5 5 ^ 3 2 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 r r r 遍历序列:1 4 3 2 5 • 图的广度遍历(BFS)

  42. 图的遍历 例 1 firstarc adjvex next vexdata ^ 1 1 2 3 4 2 2 ^ 3 3 ^ 5 4 4 ^ f f f 2 3 4 1 1 5 2 4 3 5 1 5 5 5 ^ 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 5 2 5 r r r 遍历序列:1 4 3 2 5 遍历序列:1 4 3 2 5 遍历序列:1 4 3 2 5 • 图的广度遍历(BFS)

  43. 小结 有两种遍历方法(它们对无向图,有向图都适用) 深度优先遍历 从图中某顶点v出发: 1)访问顶点v; 2)依次从v的未被访问的邻接点出发,继续对图进行深度优先遍历; 广度优先遍历 图中某未访问过的顶点vi出发: 1)访问顶点vi; 2)访问 vi 的所有未被访问的邻接点w1 ,w2 , …wk ; 3)依次从这些邻接点出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问; 由于没有规定访问邻接点的顺序,这两种遍历序列的结果都不是唯一的。

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

  45. 图的最小生成树 • 问题提出 要在n个城市间建立通信联络网, 顶点——表示城市 权——城市间建立通信线路所需花费代价 希望找到一棵生成树,它的每条边上的权值之和(即建立该通信网所需花费的总代价)最小——最小代价生成树 • 问题分析 n个城市间,最多可设置n(n-1)/2条线路。 n个城市间建立通信网,只需n-1条线路。 问题转化为:如何在可能的线路中选择 n-1 条,能把所有城市(顶点)均连起来,且总耗费(各边权值之和)最小。

  46. 图的最小生成树 • 普里姆算法的基本思想 取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。 在添加的顶点 w 和已经在生成树上的顶点 v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。 之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。

  47. 图的最小生成树 • 普里姆算法(Prim) 设 G=(V,GE)为一个具有 n 个顶点的连通网络,T=(U,TE)为构造的生成树。 (1)初始时,U = {u0},TE = ; (2)在所有 uU 、vV-U 的边(u,v)中选择一条权值最小的边,不妨设为(u,v); (3)(u,v) 加入TE,同时将 v 加入U; (4)重复(2)(3),直到 U=V 为止; u u v    v  V-U缩小 U扩大 U V-U

  48. 图的最小生成树 3 6 5 1 5 5 6 4 3 V4 V1 V6 V4 V3 V1 V3 V2 V3 V2 V3 V2 V2 V4 V2 V1 V4 V2 V1 V1 V4 V6 V3 V3 V5 V6 V5 V5 V5 V6 V6 V4 V1 V5 V6 V5 2 6 5 5 1 1 1 1 1 2 2 2 4 4 4 4 • 普里姆算法(Prim) U={ V1,V3,V6} U={ V1 } U={ V1,V3 } U={ V1,V3,V6,V4 } U={ V1,V3,V6,V4,V2 } U={ V1,V3,V6,V4,V2,V5 }

  49. 图的最小生成树 • 普里姆算法的基本思想 设置一个辅助数组,对当前 V-U 集中的每个顶点,记录和顶点集U中顶点相连接的代价最小的边: struct { VertexType adjvex; // U集中的顶点序号 VRType lowcost; // 边的权值 } closedge [ MAX_VERTEX_NUM ];

  50. 图的最小生成树 • 普里姆算法的基本思想 • 有关数据的存储结构 • 无向连通网络: MGraph G 选择权值最小的边: 设置一个一维数组closedge[ ],对 vV-U closedge[v].lowcost = min{ cost[u’,v]| u’U } //保存连接v到U中各顶点权最小边的权值 • closedge[v].adjvex = u • //用来记录该边在U中的顶点

More Related