1、数据结构图-第 7 章节目 录第 7 章 图 17.1 图的定义和基本术语 17.2 图的存储和创建 27.2.1 图的存储表示 27.2.2 图的创建 57.3 图的遍历 57.3.1 深度优先搜索 57.3.2 广度优先搜索 67.4 遍历算法的应用 77.4.1 应用问题概述 77.4.2 求一条包含图中所有顶点的简单路径 87.4.3 求距 v0 的各顶点中最短路径长度最长的一个顶点 97.5 图的连通性问题 107.5.1 无向图的连通分量和生成树 107.5.2 最小生成树 127.6 有向无环图及其应用 12第 7 章 图7.1 图的定义和基本术语1、图的特征任意两个数据元素之间
2、都可能相关。结点之间的关系是多对多的。G = (V,E)2、基本术语结点: 顶点结点间的关系:无向图:边( v, w ),v 与 w 互为邻接点,边 ( v, w )依附于顶点 v, w,边( v, w )和顶点 v, w 相关联v 的度:和 v 相关联的边的数目。有向图:弧,v 弧尾,w 弧头,顶点 v 邻接到顶点 w,顶点 w 邻接自顶点 v,弧和顶点 v,w 相关联。v 的入度:以 v 为弧头的弧的数目; v 的出度:以 v 为弧尾的弧的数目;v 的度:v 的入度与出度之和。路径、回路(环)、简单路径、简单回路(简单环)连通性:若从顶点 v 到顶点 v有路径,则称 v 和 v是连通的图的
3、规模:顶点数 n、边(弧)数 e、顶点的度(有向图:入度/出度)子图:G= (V,E), G = (V,E),若 VV 且 E E,则称 G是 G 的子图。图的分类:1)关系的方向性(无向/有向) 、关系上是否有附加的数权(图/网)有向图、无向图、有向网、无向网2)边(弧) 数:完全图(边数= n ( n-1 ) / 2 的无向图) 、有向完全图(弧数= n ( n-1)的有向图)稀疏图(enlogn)3)连通性:无向图:连通图( 任意两顶点都是连通的)、连通分量(极大连通子图) 、生成树(极小连通子图)、生成森林有向图:强/弱连通图、强连通分量、生成树(极小连通子图)、生成森林3、抽象数据类
4、型定义ADT Graph数据对象 V:V 是具有相同特性的数据元素的集合,称为顶点集。数据关系 R:R = VRVR = |v, wV 且 P(v, w), 表示从 v 到 w 的弧,谓词 P(v,w) 定义了弧的意义或信息 基本操作:CreateGraph( /*有向图,有向网,无向图,无向网*/2、邻接矩阵表示法(数组表示法)无向图/网:对称矩阵 有向图/网:非必是对称矩阵图:邻接关系用 1/0 表示网:邻接关系需要进一步反映权值,用 INFINITY 表示无穷大,反映顶点之间无邻接关系#define INT_MAX 32767 /* 最大整数 */#define INFINITY INT
5、_MAX1) 邻接矩阵typedef struct ArcCellint adj; / 顶点间关系,无权图:0- 不相邻,1-相邻/ 有权图,权值,INFINITY- 不相邻InfoType *info; / 该弧相关信息的指针ArcCell, AdjMatrixMAX_VERTEX_NUMMAX_VERTEX_NUM; 2) 图的整体结构typedef struct VertexType vexsMAX_VERTEX_NUM; /* 有效的顶点下标从 0 开始 */AdjMatrix arcs; /* 关系集 */int vexnum, arcnum; /* 顶点数、边/弧数 */Graph
6、Kind kind; /* 图的种类 */MGraph;3、邻接表表示法无向图/网:边表,表结点的个数为边数的两倍有向图/网:出边表,表结点的个数为弧数1) 邻接表的表结点typedef struct ArcNodeint adjvex; /* 弧所指向的顶点的位置 */struct ArcNode *nextarc; /* 指向下一条弧的指针 */InfoType *info;ArcNode;2) 邻接表的头结点typedef struct VNodeVertexType data; /* 顶点信息 */ArcNode *firstarc; /* 邻接表指针 */VNode, AdjList
7、MAX_VERTEX_NUM;3) 图的整体结构typedef struct AdjList vertices;int vexnum, arcnum;GraphKind kind;ALGraph;4、邻接矩阵与邻接表的对比假设图为 G,顶点数为 n,边 /弧数为 e。A 邻接矩阵 B 邻接表存储空间 O(n+n2) O(n+e)T1(n)=O(e+n2)或 T2(n)=O(e*n+n2) T1(n)=O(n+e)或 T2(n)=O(e*n)图的创建算法T1(n)是指在输入边/弧时,输入的顶点信息为顶点的编号;而 T2(n)则指在输入边/弧时,输入的为顶点本身的信息,此时需要查找顶点在图中的位置
8、无向图中求第 i 顶点的度(第 i 行之和)或(第 i 列之和)无向网中求第 i 顶点的度第 i 行/列中 adj 值不为 INFINITY 的元素个数G.verticesi.firstarc 所指向的邻接表包含的结点个数有向图中求第 i 顶点的入/出度入度:(第 i 列)出度: (第 i 行)有向网中求第 i 顶点的入/出度入度:第 i 列中 adj 值不为 INFINITY的元素个数出度:第 i 行中 adj 值不为 INFINITY的元素个数入度:扫描各顶点的邻接表,统计表结点的 adjvex 为 i 的表结点个数 T(n)=O(n+e)出度:G.verticesi.firstarc 所
9、指向的邻接表包含的结点个数统计边/弧数无向图:)无向网:G.arcs 中 adj 值不为INFINITY 的元素个数的一半无向图/网:图中表结点数目的一半有向图/网:图中表结点的数目有向图:有向网:G.arcs 中 adj 值不为INFINITY 的元素个数结论:邻接矩阵适于稠密图的存储,邻接表适于稀疏图的存储;邻接表求有向图的顶点的入度不方便,要遍历各个顶点的邻接表。7.2.2 图的创建基本过程:1) 输入图的类型,根据类型选择相应的创建算法2) 输入图的顶点数,边/弧数3) 输入并存储顶点信息4) 输入边/弧所关联的顶点对,将边或弧的信息存储到邻接矩阵/邻接表中图的存储结构不同、图的类型不
10、同,都会影响创建算法的实现细节;但是,图的总体创建流程是一致的(如上) 。示例:用邻接矩阵表示法构造有向网 GStatus CreateMDG(MGraph /* IncInfo 为 0 则各弧不含其它信息 */* 步骤 3:输入并存储顶点信息 */for ( i=0; inextarc)if (!visitedp-adjvex ) DFS(G, p-adjvex, Visit);7.3.2 广度优先搜索1、分析类似于树的层次遍历引入 visited 访问标志数组非连通图,需引入多个广度优先搜索的起始顶点引入队列保存“顶点已访问,但其邻接点未全访问”的顶点编号2、基于 ADT Graph 的
11、BFS 算法void BFSTraverse (Graph G, Status ( *Visit ) (Graph G, int v) )for ( v = 0; v nextarc )if ( !visitedp-adjvex )/* 访问顶点 u 的尚未访问的邻接点并入队 */visitedp-adjvex = TRUE; Visit(G, p-adjvex);EnQueue(Q, p-adjvex);7.4 遍历算法的应用7.4.1 应用问题概述图的深度优先遍历:1、 求一条包含图中所有顶点的简单路径(简单回路)2、 判断图中是否存在环3、 求图中通过给定顶点 vk 的简单回路 4、 判
12、断是否存在从顶点 vi 到顶点 vj 的路径5、 判别 v0 和 v1 之间是否存在一条长度为 k 的路径图的广度优先遍历:1、 判断是否存在从顶点 vi 到顶点 vj 的路径2、 求距 v0 的各顶点中最短路径长度最长的一个顶点。3、 求 v0 和 v1 之间的最短路径.4、 在顶点子集 U 中找出距离顶点 v0 最近的顶点5、 求顶点 v0 到其余每个顶点的最短路径6、 求距离顶点 v0 的最短路径长度为 k 的所有顶点7.4.2 求一条包含图中所有顶点的简单路径1、思路对于任意的有向图或无向图 G,并不一定都能找到符合题意的简单路径。这样的简单路径要求包含 G.vexnum 个顶点,且互
13、不相同。它的查找可以基于深度优先遍历。12345671234567(a)(b)在一个存在包含全部顶点的简单路径的图中,以下因素会影响该简单路径是否能顺利地查到:1) 起点的选择:如图(a),其符合题意的一条简单路径如图(b)。若起点为 1,则不能找到符合题意的简单路径;2) 顶点的邻接点次序:进一步考察图(a),即使以 2 为起点,但是 2 的邻接点选择的是1,而不是 5,此时也不能找到符合题意的解。在基于 DFS 的查找算法中,由于起点和邻接点的选取是与顶点和邻接点的存储次序以及算法的搜索次序有关,不可能依据特定的图给出特定的解决算法。因此,在整个搜索中应允许有查找失败,此时可采取回溯到上一
14、层的方法,继续查找其他路径。这样,引入数组 Path 用来保存当前已搜索的简单路径上的顶点,引入计数器 n 用来记录当前该路径上的顶点数。对 DFS 算法的修改如下:1) 计数器 n 的初始化,放在 visited 的初始化前后;2) 访问顶点时,增加将该顶点序号入数组 Path 中,计数器 n+;判断是否已获得所求路径,是则输出结束,否则继续遍历邻接点;3) 某顶点的全部邻接点都访问后,仍未得到简单路径,则回溯,将该顶点置为未访问,计数器 n-。2、算法/* 邻接矩阵表示法, 粗体字部分为在深度优先遍历上的增加或修改的步骤 */void Hamilton(MGraph G)for ( i=0
15、; inextsbling = p; /* 其它生成树的根( 前一棵的“兄弟”) */q = p; /* q 指示当前生成树的根 */DFSTree(G, v, p); /* 建立以 p 指向的结点为根结点的生成树 */void DFSTree(Graph G, int v, CSTree first = TRUE; /* 标识下一个访问的邻接点是否为第一个未访问的邻接点 */for ( w = FirstAdjVex( G, v); w; w = NextAdjVex(G, v, w) )if ( !visitedw )p = ( CSTree ) malloc(sizeof(CSNode)
16、; /* 分配孩子结点 */*p = GetVex(G, w), NULL, NULL;if ( first ) /* w 是 v 的第一个未访问的邻接点 */T-firstchild = p; first = FALSE; /*是根的第一个孩子 */ else q-nextsbling = p; /* 是上一邻接点的右兄弟结点 */q = p; /* 修改上一孩子结点的指针 */DFSTree(G, w, q);4、广度优先搜索生成树/森林算法思路引入队列,除了保存“顶点已访问,但其邻接点未全访问”的顶点编号以外,还须保存该顶点在生成树中的对应结点的指针。void BFSForest(Gra
17、ph G, CSTree for ( v=0; vnextsbling = p; /* 其它生成树的根( 前一棵的“兄弟”) */q = p; /* q 指示当前生成树的根 */EnQueue(Q, v, p);while( !QueueEmpty(Q) )/* 出队,访问出队元素的邻接点 */DeQueue(Q,u, q);first = TRUE;for ( w = FirstAdjVex( G, u) ; w ; w = NextAdjVex(G, u, w)if ( !visitedw )/* 访问顶点 u 的尚未访问的邻接点并入队 */visitedw = TRUE;p = ( CS
18、Tree ) malloc(sizeof(CSNode); /* 分配孩子结点 */*p = GetVex(G, w), NULL, NULL;if ( first ) /* w 是 v 的第一个未访问的邻接点 */q-firstchild = p; first = FALSE; /*是根的第一个孩子 */ else q-nextsbling = p; /* 是上一邻接点的右兄弟结点 */q = p; /* 修改上一孩子结点的指针 */EnQueue(Q, w, p);7.5.2 最小生成树1、概念应用的图的类型:连通网(无向带权)最小生成树:树上各边权值之和最小的生成树MST 性质2、普里姆
19、算法已知网 N = (V, E), 普里姆算法的求解过程是最小生成树不断壮大的过程初始:最小生成树仅包括一个顶点 U=u0,无边 TE = ;循环处理:从 V-U 中选择一个顶点 v0, 选择条件:所有 uU, vV-U 的边(u , v)E 中,找一条权值最小的边( u, v0)v0 加入到 U, ( u, v0)加入到 TE循环结束条件:V = U3、克鲁斯卡尔算法克鲁斯卡尔算法的求解过程是连通分量不断合并的过程初始:n 个顶点组成 n 个连通分量循环处理:在两连通分量中选择一条边( u, v)选择条件:权值最小,连接两连通分量循环结束条件:一个连通分量7.6 有向无环图及其应用1、拓扑排序AOV-网1) 算法 1:利用入度增加一个存放顶点入度的数组( indegree),初始反映顶点的实际的入度;选择一数据结构(栈或队列 )保存入度为 0 的顶点位置( 序号) ;引入输出顶点的计数器,初始为 0循环处理:从栈(或队列)中取得一个入度为 0 的顶点;输出该顶点,将输出顶点的计数器加 1将该顶点的出边关联的各顶点的入度减 1,当入度为 0 则保存至栈(或队列);图中的所有顶点没有全部输出,则说明有环2) 算法 2:DFS前提条件:有向无环图算法:在 DFS 基础上变形,访问完起点的各邻接点后再输出该点。结果:逆拓扑排序。