1 / 89

第 7 章 图

第 7 章 图. 2014年8月24日. 第 7 章 图. 7.1 图的基本概念 7.2 图的存储结构 7.2.1 邻接距阵表示法 7.2.2 邻接表表示法 7.3 图的遍历 7.3.1 深度优先搜索 7.3.2 广度优先搜索 7.4 图的应用 7.4.1 连通图的最小生成树 7.4.2 拓扑排序. 7.1 图的基本概念. 一、 现实中的图. 图最常见的应用是在交通运输和通信网络中找出造价最低的方案。通信网络示例如下图所示:. 二、 图的定义.

azana
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章 图 2014年8月24日

  2. 第7章 图 7.1 图的基本概念 7.2 图的存储结构 7.2.1 邻接距阵表示法 7.2.2 邻接表表示法 7.3 图的遍历 7.3.1 深度优先搜索 7.3.2 广度优先搜索 7.4 图的应用 7.4.1 连通图的最小生成树 7.4.2 拓扑排序

  3. 7.1 图的基本概念 一、现实中的图 图最常见的应用是在交通运输和通信网络中找出造价最低的方案。通信网络示例如下图所示:

  4. 二、图的定义 图G是由一个顶点集V和一个边集E构成的数据结构。记为二元组形式: G= (V, E) 其中: • V是由顶点构成的非空有限集合,记为:V={V0, V1, V2, …Vn-1} • E是由V中顶点的对偶构成的有限集合,记为:E={(V0, V2), (V3, V4), … },若E为空,则图中只有顶点而没有边。 其中对偶可以表示成: • (Vi, Vj)—无序的对偶称为边,即(Vi, Vj)=(Vj, Vi) ,其图称为无向图 • <Vi, Vj>—有序的对偶称为弧,即<Vi, Vj> ≠<Vj, Vi>,则称Vi为弧尾,称Vj为弧头,该图称为有向图

  5. 有向图和无向图示例: 无向图G1的二元组表示: V(G1)={V1, V2, V3, V4} E(G1)={(V1, V2),(V1, V3),(V1, V4),(V2, V3),(V2, V4),(V3, V4)} 有向图G3的二元组表示: V(G3)={V1, V2, V3} E(G3)={<V1, V2>,<V1, V3>,<V2, V3>,<V3, V2>}

  6. 1.邻接点 • 在无向图中,若存在一条边(Vi, Vj),则称Vi和Vj互为邻接点(Adjacent) • 在有向图中,若存在一条弧<Vi, Vj >,则称Vi为此弧的起点,称Vj为此弧的终点,称Vi邻接到Vj,Vj邻接自Vi,Vi和Vj互为邻接点。

  7. 2.顶点的度、入度和出度 • 在无向图中,与顶点v相邻接的边数称为该顶点的度,记为D(v)。 • 在有向图中,顶点v的度又分为入度和出度两种: • 以顶点v为终点(弧头)的弧的数目称为顶点v的入度,记为ID(v); • 以顶点v为起点(弧尾)的弧的数目称为顶点v的出度,记为OD(v); • 有向图顶点v的度为该顶点的入度和出度之和,即 D(v)=ID(v)+OD(v)

  8. 无论是有向图还是无向图,一个图的顶点数n、边(弧)数e和每个顶点的度di之间满足以下的关系式:无论是有向图还是无向图,一个图的顶点数n、边(弧)数e和每个顶点的度di之间满足以下的关系式: • 即在有向图或无向图中: 所有顶点度数之和 :边数 = 2 :1

  9. 3.完全图、稠密图和稀疏图 • 在图G中: • 若G为无向图,任意两个顶点之间都有一条边,称G为无向完全图。顶点数为n,无向完全图的边数: e=Cn2 =n(n1)/2 • 若G为有向图,任意两个顶点Vi, Vj之间都有弧<Vi,Vj> 、<Vj,Vi>,称G为有向完全图。如顶点数为n,有向完全图的弧数: e=Pn2 =n(n1) • 例如,无向图G1就是4个顶点无向完全图。 • 若一个图接近完全图,则称其为稠密图;反之,若一个图含有很少条边或弧(即e<<n2),则称其为稀疏图。

  10. 4.子图 • 若有图G=(V, E)和G′=(V′, E′) • 且V′是V的子集,即V′V , E′是E的子集,即E′E • 则称图G′为图G的子图。

  11. 5.路径、回路和路径长度 • 在无向图G中,若存在一个顶点序列(Vp, Vi1 , Vi2 , … , Vin, Vq),使(Vp, Vi1),(Vi1, Vi2),…,(Vin, Vq)均为图G的边,则该称顶点的序列为顶点Vp到顶点Vq的路径。若G是有向图,则路径是有向的。 • 路径长度定义为路径上的边数或者弧的数目。 • 若一条路径中不出现重复顶点,则称此路径为简单路径。 • 若一条路径的起点和终点相同(Vp=Vq)称为回路或环。 • 除了起点和终点相同外,其余顶点不相同的回路,称为简单回路或简单环。

  12. 例如,在无向图G1中: • 顶点序列(V1, V2, V3, V4)是一条从顶点V1到顶点V4,长度为3的简单路径; • 顶点序列(V1, V2, V4, V1, V3)是一条从顶点V1到顶点V3,长度为4的路径,但不是简单路径; • 顶点序列(V1, V2, V3, V1)是一条长度为3的简单回路。 • 在有向图G3中: • 顶点序列(V2, V3, V2)是一个长度为2的有向简单环。

  13. 6.连通、连通分量和连通图、生成树 • 在无向图G中: • 如果从顶点Vi到顶点Vj至少有一条路径,则称Vi与Vj是连通的。 • 如果图中任意两个顶点都连通,则称G为连通图,否则称为非连通图。 • 在非连通图G中,任何一个极大连通子图称为G的连通分量。 • 任何连通图的连通分量只有一个,即其自身,而非连通图有多个连通分量。 • 在一个连通图中,含有全部顶点的极小(边数最少)连通子图,称为该连通图的生成树。(包含图的所有 n 个结点,但只含图的 n-1 条边。在生成树中添加一条边之后,必定会形成回路或环)

  14. A B F G E A B E F G I J K I J C D K A B A B A B C D C D C D C D • 图G1和G2为连通图 • 非连通图G4的三个连通分量 • 非连通图G4 • 连通图G5 • 连通图G5的两棵生成树

  15. 7.强连通、强连通分量和强连通图 • 在有向图G中: • 存在从顶点Vi到顶点Vj的路径,也存在从顶点Vj到顶点Vi的路径,则称Vi与Vj是强连通的。 • 如果图中任意两个顶点都是强连通,则称G为强连通图,否则称为非强连通图。 • 在非强连通图G中,任何一个极大强连通子图称为G的强连通分量。 • 任何强连通图的强连通分量只有一个,即其自身,而非强连通图有多个强连通分量。

  16. 有向图G和强连通分量示例:

  17. 8.权、带权图、有向网和无向网 • 在一个图中,各边(或弧)上可以带一个数值,这个数值称为权。 • 这种每条边都带权的图称为带权图或网 • 有向网:带权有向图 • 无向网:带权无向图

  18. 7.2 图的存储表示 • 图需存储的信息: • 各顶点的数据 • 各个边(弧)的信息,包括: • 哪两个顶点有边(弧) • 若有权要表示出来 • 顶点数、边(弧)数

  19. a[i][j]={ a[i][j]={ 0 vi与vj无边 0 vi到vj无弧 1 vi与vj有边 1 vi到vj有弧 ∞或0 vi与vj无边(或vi到vj无弧) a[i][j]={ w vi与vj有边(或vi到vj有弧) 7.2.1 邻接矩阵表示法 • 顶点数据存储: • 一维数组(顺序存储) • 边(弧)信息的存储: • 邻接矩阵:图中n个顶点之间相邻关系的n阶方阵(即二维数组a[n][n]) • 邻接矩阵中元素值情况(规定自身无边、无弧): • 无向图 • 有向图 边(弧)上的权 • 网

  20. V4 V4 V5 V5 V1 V1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 ∞ 2 ∞ 4 ∞ 2∞ 1 ∞ 5 ∞ 1 ∞ 3 1 4 ∞ 3 ∞ ∞ ∞ 5 1 ∞ ∞ V2 V2 V3 V3 V1 V1 V2 V2 V1 V1 V2 V2 V3 V3 V3 V3 邻接矩阵表示 V4 V4 V5 V5 V4 V4 V5 V5 2 1 4 5 邻接矩阵表示 1 3 1、举例 • 无向图 • 特点: • 对称 • 行或列方向的非零元素(或1)的个数为此顶点的度 • 无向网

  21. V4 V3 V1 V2 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 V1 V2 邻接矩阵表示 V3 V4 V1 V2 V3 V4 1、举例 • 有向图 • 特点: • 不一定对称 • 行方向的非零元素(或1)的个数为此顶点的出度 • 列方向的非零元素(或1)的个数为此顶点的入度

  22. 2、邻接矩阵法特点 邻接矩阵法优点: 容易实现图的操作,如:求某顶点的度、判断顶点之间是否 有边(弧)、找顶点的邻接点等等。 n个顶点需要n*n个单元存储边(弧);空间效率为O(n2)。 对稀疏图而言尤其浪费空间。 邻接矩阵法缺点:

  23. 3、邻接矩阵存储的图类型定义 # define MAX 100 /* MAX为图中顶点最多个数 */ typedef int vextype; /* vextype为顶点的数据类型 */ typedef struct{ vextype vex[MAX]; /* 一维数组存储顶点信息 */ int arc[MAX][MAX]; /*邻接矩阵存储边(弧)信息 */ int vn, en; /* vn顶点数和en边数 */ }MGraph; /* 图类型 */ 注:MGraph既可以表示有向图、无向图,也可以表示有整型权的网

  24. 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0 1 2 3 4 4、建图运算 建图——就是完成图类型变量中各个成员值的创建过程。 例:建一个如图所示的无向图 执行时输入数据: 5 6 0 1 2 3 4 0 1 0 3 1 2 1 4 2 3 2 4 分析: 各顶点信息:键盘输入 各边信息:邻接矩阵,顶点间有边值为1,无边值为0 顶点数、边数:键盘输入

  25. 算法实现(无向图) void CreateGraph(MGraph *g) { int i, j, v, e; scanf("%d %d", &g->vn, &g->en); /*输入顶点数和边数*/ for(v=0;v<g->vn;v++) scanf("%d", &g->vex[v]); /*顶点数据输入*/ for(i=0;i<g->vn;i++) for(j=0;j<g->vn;j++) g->arc[i][j]=0; /*邻接矩阵的初始值都为0*/ for(e=0;e<g->en;e++) /*共有g->en条边*/ { scanf("%d %d", &i, &j); /*指明有边的两个顶点的序号*/ g->arc[i][j]=1; /*有边赋值为1*/ g->arc[j][i]=1; /*建有向图时此句不要*/ } }

  26. 7.2.2 邻接表法 • 是顺序与链接相结合的图的存储方式 • 所有顶点组成一个数组,为每个顶点建立一个单链表 • 有两部分组成: • 表头——顶点数组(存放顶点信息) • 表体——单链表(存放与顶点相关的边或弧的信息)

  27. V1 V1 V2 V2 0 0 V1 V5 V4 V3 V2 V3 V2 V5 V4 V1 2 3 4 1 3 1 0 1 4 0 2 2 ∧ ∧ ∧ ∧ ∧ V3 V3 1 1 2 2 邻接表表示 V4 V4 V5 V5 3 3 4 4 2 1 1 3 0 4 2 4 3 0 2 2 1 5 2 5 4 1 1 1 3 3 4 2 1 ∧ ∧ ∧ ∧ ∧ 1 4 5 邻接表表示 1 3 1、举例 与顶点V1相邻接的顶点在数组中的下标 • 无向图 • 顶点的度:该顶点所在单链表中表结点个数 权值 • 无向网

  28. V1 V4 V3 V2 ∧ 1 2 3 0 ∧ ∧ ∧ 0 1 2 邻接表表示 3 V1 V2 V3 V4 1、举例 以顶点V1为始点的弧的终点顶点在数组中的下标 • 有向图 • 顶点的出度 • 该顶点所在单链表中表结点个数 • 顶点的入度 • 查询整个邻接表中的表结点,与该顶点的序号(数组下标)一致的表结点个数

  29. 2、邻接表法特点 邻接表的优点: 空间效率高;容易寻找顶点的邻接点; 邻接表的缺点: 判断任意两顶点间是否有边或弧,需搜索两结点对应的单链表,没有邻接矩阵方便。

  30. adjvex next adjvex w next 3、邻接表存储的图类型定义 (1)表(边)结点——表示边(或弧)信息的链表中结点 表结点: 邻接点序号(下标) 下一个邻接点地址 权值 typedef struct arcnode{ int adjvex; struct arcnode *next; } ArcNode, *Arc; 表结点类型

  31. vertex firstarc 3、邻接表存储的图类型定义 (2)顶点结点类型 顶点结点: 链表头指针(指向第一个表结点) 顶点信息 typedef struct vexnode{ vextype vertex; ArcNode *firstarc; } VexNode; 顶点结点类型 (3)图的邻接表类型 typedef struct { VexNode adjlist[MAX]; /*顶点结点所在数组*/ int vn, en; } ALGraph;

  32. 0 3 2 1 ∧ 0 3 1 2 ∧ ∧ ∧ 0 1 2 3 0 1 2 3 4、建图运算 建图——就是完成图类型变量中各个成员值的创建过程。 adjvex 例:建一个如图所示的有向图 next firstarc vertex 执行时输入数据: 4 4 0 1 2 3 0 2 0 1 2 3 3 0 分析: 各顶点信息:顶点数据键盘输入 各链表:若顶点有出度弧,创建表结点,头插入链表 顶点数、边数:键盘输入

  33. 算法实现(有向图) void CreateAGraph(ALGraph *g) { int i, j, v, e; ArcNode* p; scanf("%d %d", &g->vn, &g->en); /*输入顶点数和边数*/ for(v=0;v<g->vn;v++) /*顶点数组元素值的获得*/ { scanf("%d",&g->adjlist[v].vertex); /*顶点数据键盘输入*/ g->adjlist[v].firstarc=NULL; } for(e=0;e<g->en;e++) /*共有g->en条边*/ { scanf("%d %d", &i, &j); /*i j有弧,i、j为顶点序号*/ p=(ArcNode*)malloc(sizeof(ArcNode)); p->adjvex=j; /*把值为j的结点头插入到i顶点的链表中*/ p->next=g->adjlist[i].firstarc; g->adjlist[i].firstarc=p; } } 思考: 建无向图如何修改?

  34. 0 A 1 4 1 B 0 4 5 2 C 3 5 3 D 2 5 4 E 0 1 5 F 1 2 3 补例:图的邻接表存储表示 B C A D F E

  35. A B C D E  0 1 2 3 4 1 4  2  3  0 1  2 有向图的邻接表 A B E C D 可见,在有向图的邻接表中不易找到指向该顶点的弧

  36. A B C D E 3  0 1 2 3 4 3 0  4  2  0  A 有向图的逆邻接表 B E 在有向图的逆邻接表中,对每个顶点,链接的是指向该顶点的弧 C D

  37. 7.3 图的遍历 从图中某个顶点出发遍历图,访遍图中其余顶点,并且使图中的每个顶点仅被访问一次的过程。 图的遍历有两种方法: 深度优先搜索遍历(DFS) 广度优先搜索遍历(BFS)。 它们对无向图和有向图都适用。

  38. 7.3.1 连通图的深度优先搜索遍历 1.深度优先搜索(dfs)的基本思想 • 首先访问初始出发点V0,并将其标记设置为已访问; • 然后任选一个与V0相邻接且没有被访问的邻接点V1作为新的出发点,访问V1之后,将其标记设置为已访问; • 再以V1为新的出发点,继续进行深度优先搜索,访问与V1相邻接且没有被访问的任一个顶点V2; • 重复上述过程,若遇到一个所有邻接点均被访问过的顶点,则回到已访问顶点序列中最后一个还存在未被访问的邻接点的顶点,再从该顶点出发继续进行深度优先搜索,直到图中所有顶点都被访问过,搜索结束。

  39. V0 V1 V2 V3 V4 V5 V6 V7 dfs遍历举例: dfs序列 不唯一! dfs遍历部分顶点序列(V0顶点出发) : (1)V0、 V1、 V2、 V4、 V3、 V5、 V6、 V7 (2)V0、 V1、 V2、 V4、 V3、 V7、 V5、 V6 (3)V0、 V1、 V3、 V5、 V6、 V7、 V2、 V4

  40. 2 . dfs递归算法 • 算法描述: 访问开始顶点(如 v); 寻找 v 顶点未被访问的第一个邻接顶点(如 w); 并把 w 作为开始顶点继续深度优先搜索遍历(递归思想); 直到所有顶点被访问; • 其中处理: (1)访问顶点:自定义访问函数 visit() (2)未被访问的邻接点:定义一个标记数组:int visited[MAX] visited[i]= 0 i号顶点未被访问 1 i号顶点已被访问 注意:由于函数是递归的,数组定义成外部数组 (3)第一个邻接点:按邻接距阵或邻接表中边存储的顺序

  41. 2 . dfs递归算法 邻接距阵存储结构的图 • 函数实现: 形参:图变量地址,开始顶点的序号(下标) 返回值:无 void dfs(MGraph *g, int v) { int j, w; visit(g, v); /*访问v号顶点*/ visited[v]=1; /*v号顶点为已访问*/ for(j=0; j<g->vn; j++) /*找所有的邻接顶点*/ if( g->arc[v][j]==1 && visited[j]==0) /*v号顶点与j号顶点 间有边,且j号顶点为被访问*/ { w=j; dfs(g, w); } } 起始顶点的序号(下标) void visit (MGraph *g, int v) { printf("\n %d", g->vex[v]); }

  42. 邻接矩阵存储结构: V0 无向图: V1 V0 0 V2 V1 V4 1 V2 2 V3 V3 3 V4 4 按算法实现的dfs遍历举例: V0顶点出发遍历结果(唯一) : V0、 V1、 V2、 V3、 V4 V2顶点出发遍历结果(唯一) : V2、 V1、 V0、 V4、 V3

  43. 设计程序创建邻接矩阵的无向图,并用dfs图中顶点信息并输出:设计程序创建邻接矩阵的无向图,并用dfs图中顶点信息并输出: 宏定义及数据类型: #include <stdlib.h> #define MAX 20 /*图顶点最多不超过20*/ typedef int vextype; /*图顶点为int型*/ typedef struct{ vextype vex[MAX]; int arc[MAX][MAX]; int vn, en; } MGraph; /*邻接矩阵图类型*/ int visited[MAX]; /*访问标记数组*/

  44. 函数定义: void CreateGraph(MGraph *g) /*创建无向图*/ { …… } void visit(MGraph *g, int v) /*访问图中某个顶点*/ { …… } void dfs(MGraph *g, int v) /*dfs遍历图*/ { …… } main() /*主函数*/ { MGraph mg; /*mg为图结构变量/ int i, start; /*start开始顶点序号*/ CreateGraph(&mg); for(i=0; i<mg.vn; i++) /*访问标记数组置初值0*/ visited[i]=0; scanf("%d", &start); dfs(&mg, start); }

  45. 7.3.2 连通图的广度优先搜索遍历 1.广度优先搜索(bfs)的基本思想 • 从图G=(V, E)的某个起始点v0出发,首先访问起始点v0,接着依次访问v0所有邻接点; • 再找v0的第一个邻接顶点(如 w1),再依次访问w1顶点的所有未被访问的邻接顶点; • 再找v0的第二个邻接顶点(如 w2),再依次访问w2顶点的所有未被访问的邻接顶点; • …… • 直到图中所有顶点都被访问到为止。

  46. V0 V1 V2 V3 V4 V5 V6 V7 bfs遍历举例: bfs序列 不唯一! bfs遍历部分顶点序列(V0顶点出发) : (1)V0、 V1、 V3、 V5、 V2、 V7、 V6、 V4 (2)V0、 V5、 V3、 V1、 V6、 V7、 V2、 V4 bfs辅助工具 : 因邻接点的出发的次序是“先被访问先出发”的原则, 故用队列来管理邻接点出发的次序。(图示讲解!)

  47. 2 . bfs算法 • 算法描述: (1) 定义一个队列Q并初始化 (2) 开始顶点(如 v)入队,置访问标记为1; (3) 当队列不空时,反复做: (a)队头顶点出队→w ,访问w; (b)寻找w的所有未被访问的邻接顶点入队,置访问标记为1;

  48. 2 . bfs算法 邻接表存储结构的图 • 函数实现: 形参:图变量地址,开始顶点的序号(下标) 返回值:无 void bfs(ALGraph *g, int v) { int i, w; ArcNode *p; SeqQueue Q; /*循环队列*/ InitQueue(&Q); /*初始化队列*/ EnQueue(&Q, v); /*入队*/ visited[v]=1; /*置v号顶点访问标记为1*/ while(!EmptyQueue(&Q)) /*队列不空*/ { w=DeQueue(&Q);/*出队*/ visit(g, w);/*访问w号顶点,自定义函数*/ p=g->adjlist[w].firstarc; /*w号顶点的第一个邻接点地址*/ 起始顶点的序号(下标)

  49. while(p!=NULL) { i=p->adjvex; /*i为邻接顶点的序号(下标)*/ if(visited[i]==0) { EnQueue(&Q, i); visited[i]=1; } p=p->next; } /*找所有未访问的邻接点的循环*/ } /*队列循环*/ } /*函数结束*/

  50. 无向图: 邻接表存储结构: V0 3 0 4 1 4 2 0 1 3 3 ∧ ∧ ∧ ∧ ∧ V0 V1 0 V1 1 V2 2 V2 V3 V4 3 V4 4 V3 按算法实现的bfs遍历举例: V0顶点出发遍历结果(唯一) : V0、 V1、 V4、 V3、 V2 V2顶点出发遍历结果(唯一) : V2、 V3、 V1、 V4、 V0

More Related