2.1k likes | 2.3k Views
第七章 图. 7.1 图的定义和术语 7.2 图的存储结构 7.3 图的遍历 7.4 图的连通性问题 7.5 有向无环图及其应用 7.6 最短路径. 7.1 图的定义和术语. 图( Graph) —— 由一个 顶点集 V 和一个 边集 E 构成的数据结构。 Graph = (V, E ) 其中, V = { x | x 某个数据对象 } 是非空有限的顶点集合; E = {( x , y ) | x , y V }
E N D
第七章 图 • 7.1 图的定义和术语 • 7.2 图的存储结构 • 7.3 图的遍历 • 7.4 图的连通性问题 • 7.5 有向无环图及其应用 • 7.6 最短路径
7.1 图的定义和术语 • 图(Graph)——由一个顶点集V和一个边集E构成的数据结构。 • Graph = (V, E ) • 其中,V = { x | x 某个数据对象} 是非空有限的顶点集合; E = {(x, y) | x, y V } 或 E = {<x, y> | x, y V && Path (x, y)} 是有限的顶点之间关系的集合,(x,y)也叫做边(edge)集合,它是无方向的; Path (x, y)表示从 x 到 y 的一条单向通路, 它是有方向的,所以<x,y>也叫做弧(arc)的集合,x称为弧尾或始点,y称为弧头或终点.
7.1 图的定义和术语 • 有向图——有向图G是由两个集合V(G)和E(G)组成的 其中:V(G)是顶点的非空有限集 E(G)是有向边(也称弧)的有限集合,弧是顶点的有序对,记为<v,w>,v,w是顶点,v为弧尾,w为弧头 • 无向图——无向图G是由两个集合V(G)和E(G)组成的 其中:V(G)是顶点的非空有限集 E(G)是边的有限集合,边是顶点的无序对,记为(v,w)或(w,v),并且(v,w)=(w,v)
2 4 5 1 3 6 G1 例7.2 1 5 7 3 2 4 6 G2 7.1 图的定义和术语 例7.1 图G1中:V(G1)={1,2,3,4,5,6} E(G1)={<1,2>, <2,1>, <2,3>, <2,4>, <3,5>, <5,6>, <6,3>} 图G2中:V(G2)={1,2,3,4,5,6,7} E(G1)={(1,2), (1,3), (2,3), (2,4),(2,5), (5,6), (5,7)}
7.1 图的定义和术语 • 无向完全图(Completed graph) —n个顶点的无向图有n(n-1)/2条边(最大边数是n(n-1)/2) • 有向完全图——n个顶点的有向图,有n(n-1)条边(最大边数是n(n-1) ) • 稀疏图(sparse graph):边或弧很少的图,通常边数 <nlog2n • 稠密图(Dense graph) • 无向图中,边数接近n(n-1)/2 ; • 有向图中,边数接近n(n-1)
7.1 图的定义和术语 有向图 有向完全图 无向 完全图 无向图(树) 完全图
7.1 图的定义和术语 • 权——与图的边或弧相关的数 • 网——带权的有向图叫有向网,带权的无向图叫无向网
7.1 图的定义和术语 • 子图——如果图G(V,E)和图G’(V’,E’),满足: • V’V,E’E • 则称G‘为G的子图
7.1 图的定义和术语 • 邻接点(或相邻点),关联 如果 e=(u, v) 是 E(G) 中的一条边,则称 u 与 v 互为邻接顶点或相邻顶点;称边e与顶点u ,v 关联; 如果 e=<u, v>是 E(G) 中的一条弧,则称 u 邻接到v,v邻接于u,也称e与u,v关联;称弧e与顶点u ,v 关联;
7.1 图的定义和术语 • 顶点的度(于树的度不同) • 无向图中,顶点的度为与每个顶点相连的边数,记作TD(v) • 有向图中,顶点的度分成入度与出度 • 入度:以该顶点为头的弧的数目,记为ID(v) • 出度:以该顶点为尾的弧的数目,记为OD(v) • 一个顶点的度数等于该顶点的入度与出度之和,即TD(v)=OD(v)+ID(v)
7.1 图的定义和术语 • 路径——路径是顶点的序列V={Vi0,Vi1,……Vin},满足(Vij-1,Vij)E 或 <Vij-1,Vij>E,(1<jn) • 路径长度——沿路径边的数目或沿路径各边权值之和 • 简单路径——序列中顶点不重复出现的路径 • 回路(环)——第一个顶点和最后一个顶点相同的路径 • 简单回路(简单环)——除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路
7.1 图的定义和术语 V0V1V3V0 V0V1V3V2 V0V1V3V2V0V1V2
无向图G的三个连通分量 无向图G A B A B F G E F G E H I J I J H K L K L M M C D C D 7.1 图的定义和术语 • 连通图与连通分量 在无向图中, 若从顶点v1到顶点v2有路径, 则称顶点v1与v2是连通的。如果图中任意一对顶点都是连通的, 则称此图是连通图。非连通图的极大连通子图叫做连通分量。
7.1 图的定义和术语 • 强连通图与强连通分量 在有向图中,若对于每一对顶点vi和vj, 都存在一条从vi到vj和从vj到vi的路径, 则称此图是强连通图。非强连通图的极大强连通子图叫做强连通分量。 有向图G 有向图G的两个 强连通分量 强连通图
无向图G 无向图G的生成树 A B A B E E H H M M C D C D 7.1 图的定义和术语 • 生成树:是一个极小连通子图,它含有图中全部n个顶点,但只有n-1条边。 • 如果在生成树上添加1条边,必定构成一个环 • 若图中有n个顶点,却少于n-1条边,必为非连通图。
有向图G 有向图G的生成森林 7.1 图的定义和术语 • 生成森林: 由若干棵生成树组成,含全部顶点,但构成这些树的边是最少的。
7.1 图的定义和术语 本章只讨论简单图,有两类图形不在本章讨论之列:
图的抽象数据类型定义 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: }
基本操作 CreatGraph(&G, V, VR) // 按定义(V, VR) 构造图 DestroyGraph(&G): // 销毁图 LocateVex(G, u); // 若G中存在顶点u,则返回该顶点在 图中“位置”;否则返回其它信息。 GetVex(G, v);// 返回 v 的值。 PutVex(&G, v, value);// 对 v 赋值value。
基本操作 FirstAdjVex(G, v); // 返回 v 的“第一个邻接点” 。若该顶点 //在 G 中没有邻接点,则返回“空”。 NextAdjVex(G, v, w); // 返回 v 的(相对于 w 的) “下一个邻接点”。若 w 是 v 的最后一个邻接点,则返回“空”。
基本操作 InsertVex(&G, v); //在图G中增添新顶点v。 DeleteVex(&G, v); // 删除G中顶点v及其相关的弧。 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一次且仅一次。
7.2 图的存储结构 图的四种常用的存储形式: • 邻接矩阵和加权邻接矩阵(labeled adjacency matrix) • 邻接表 • 十字链表 • 邻接多重表
一、(加权)邻接矩阵(labeled adjacency matrix) 设图 G= <V, E>是一个有 n 个顶点的图,则图的邻接矩阵是一个二维数组 G.arcs[n][n],定义:
无向图G1 有向图G2
一、(加权)邻接矩阵(continue) 从邻接矩阵中可以反映图的许多特征: • 无向图 (1) 对称矩阵(可采用压缩存储); (2) 每一行(或列)1的个数是该顶点的度; (3) 主对角线全为0(简单图);
一、(加权)邻接矩阵(continue) • 有向图 (1) 每一行1的个数是该顶点的出度; (2) 每一列1的个数是该顶点的入度; (3) 主对角线全为0(简单图); • 有向图的邻接矩阵不一定对称。
Wij <vi, vj> 或(vi, vj)∈VR G.arcs[ i ][ j ]= ∞ 无边(弧) N 5 v1 v2 4 8 3 7 9 v3 v6 6 1 5 v4 v5 5 一、(加权)邻接矩阵(continue) 网络的邻接矩阵 定义为: 以有向网为例: 顶点表: (v1 v2v3 v4 v5 v6 ) 邻接矩阵: ∞5∞ 7∞∞ ∞∞4∞∞∞ 8∞∞∞ ∞9 ∞∞ 5 ∞∞6 ∞∞∞ 5∞∞ 3∞∞ ∞1∞ N.Edge =
一、(加权)邻接矩阵(continue) 容易实现图的操作,如:求某顶点的度、判断顶点之间是否有边(弧)、找顶点的邻接点等等。 n个顶点需要n*n个单元存储边(弧);空间效率为O(n2)。 对稀疏图而言尤其浪费空间。 邻接矩阵法优点: 邻接矩阵法缺点:
用邻接矩阵表示的图的存储表示(P161) #define INFINITY INT_MAX #define MAX_VERTEX_NUM 20 typedef enum{DG, DN,UDG,UDN} GraghKind; typedef struct ArcCell { // 弧的定义 VRType adj; // VRType是顶点关系类型。 // 对无权图,用1或0表示相邻否; // 对带权图,则为权值类型。 InfoType *info; // 该弧相关信息的指针 } ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
用邻接矩阵表示的图的存储表示(continue) typedef struct { // 图的定义 VertexType vexs[MAX_VERTEX_NUM];// 顶点信息 AdjMatrix arcs; // 弧的信息 int vexnum, arcnum; // 顶点数,弧数 GraphKind kind; // 图的种类标志 } MGraph;
例:用邻接矩阵生成无向网的算法(参见教材P162)例:用邻接矩阵生成无向网的算法(参见教材P162) Status CreateUDN(Mgraph &G){//用邻接矩阵表示 return OK; } for(k=0;k<G.arcnum;++k){ //给弧赋初值(循环次数=弧数) scanf(&v1, &v2, &w); i=LocateVex(G,v1); j=LocateVex(G,v2); //找到两顶点在矩阵中的位置(n次) G.arcs[i][j].adj=w; //输入对应权值 If(IncInfo) Input(*G.arcs[i][j].info); G.arcs[j][i] = G.arcs [i] [j]; //无向网是对称矩阵 } scanf(&G.vexnum, &G.arcnum, &IncInfo); //输入总顶点数,总弧数和信息 for(i=0;i<G.vexnum,;++i) scanf(&G.vexs[i] );//输入顶点值,存入一维向量中 for(i=0; i<G.vexnum; ++i) //对邻接矩阵n*n个单元初始化,adj=∞,info=NULL for(j=0;j<G.vexnum;++j) G.arcs[i][j]={INFINITY, NULL}; 对于n个顶点e条弧的网, 建网时间效率 = O(n2+n+e*n)
图的部分操作在邻接矩阵上的实现 int firstAdjVex(Mgraph G, int v) { //返回顶点v的第一个邻接点,G为无向图 for (k=0;k<G.vexnum; k++) if (G.adj [v][k]>0 ) break; if (k>=G.vexnum) k= -1; // 无邻接点 return k; }// end firstAdjVex( )
二、 邻接表 (Adjacency List) v2 v1 3 1 ^ v3 4 4 2 2 0 0 ^ ^ v4 v4 v5 4 3 2 3 1 1 ^ ^ • 无向图的邻接表 为每个顶点建立一个单链表,第i个单链表中的结点表示与顶点vi相邻的顶点 注:邻接表不唯一,因各个边结点的链入顺序是任意的。
data adjvex adjvex data firstarc nextarc nextarc firstarc 二、 邻接表 (Adjacency List) 头结点: data: 结点的数据域,保存结点的数据值。 firstarc: 结点的指针域,给出自该结点出发的的第一条边 的边结点的地址。 表结点: adjvex: 该边或弧所指向的顶点的位置. nextarc: 指向下一条边或弧的指针.
v2 v1 3 1 ^ v3 4 4 2 2 0 0 ^ ^ v4 v4 v5 3 4 3 2 1 1 ^ ^ 二、 邻接表 (Adjacency List) • 在无向图的邻接表中,如何求每个顶点的度? 第 i个链表中的结点个数是顶点 i 的度 • 若顶点数为n,边数为e时,则在无向图中共需多少 个结点? n个头结点,2e个表结点
V1 V2 3 ^ v2 v1 V1 2 1 ^ V3 0 ^ V2 ^ V4 0 ^ 3 ^ V3 v3 v4 2 ^ V4 0 ^ • 有向图的邻接表和逆邻接表 • 在有向图的邻接表中,第 i个单链表链接的边都是顶点 i发出的边。也叫做出边表。 • 在有向图的逆邻接表中,第 i个单链表链接的边都是进入顶点i的边。也叫做入边表。 邻接表(出边) 逆邻接表(入边) 0 1 2 3 0 1 2 3 用邻接表表示有向图时,若不考虑逆邻接表,只需 n 个顶点结点,e 个边结点。
网络 (带权图) 的邻接表 带权图的边结点中保存该边上的权值 cost
adjvex info nextarc 邻接表表示的图的存储结构(P163) //边结点定义 typedef struct ArcNode { int adjvex; // 该弧所指向的顶点的位置 struct ArcNode *nextarc; // 指向下一条弧的指针 InfoType *info; // 该弧相关信息的指针 } ArcNode;
data firstArc 邻接表表示的图的存储结构(P163) //头结点定义 typedef struct VNode { VertexType data; // 顶点信息 ArcNode *firstarc; // 指向第一条依附该顶点的弧 } VNode, AdjList[MAX_VERTEX_NUM];
邻接表表示的图的存储结构(P163) //图的定义 typedef struct { AdjList vertices; int vexnum, arcnum; int kind; // 图的种类标志 } ALGraph;
图的操作在邻接连表的上的实现 int firstAdjVex(ALGraph ghead ,int v) { return ghead[v]->firstArc.adjvex; }// end firstAdjVex( ) int nextAdjVex(ALGraph ghead,int v, int w) { p=ghead[v]->firstArc; while (p&&p->adjvex!=w) p=p->next; if (!p) return 0; else return p->next->adjvex; }// end nextAdjVex( ) 找某顶点的所有邻接点的时间复杂度是O(e)
如何建立邻接表(以有向图为例) • 算法思想: • 首先将邻接表表头数组初始化,vertex域初始化为i,firstarc初始化为NULL; • 然后对读入的每一条弧<i,j>,产生一个表结点,adjver域置为j,并将结点链接到邻接表的第i个链表中。
算法如下: Void GreatAdjList(ALGraph &G) { ArcNode *p; sacnf(“%d%d”,&n,&e); G.vexnum=n; G.arcnum=e; for(i=0; i<n; i++) { G.vertices[i].vertex=i; G.vertices[i].firstarc=NULL;} for(k=0; k<e; k++){ scanf(i,j); p=(ArcNode *)malloc(sizeof(ArcNode)); p->adjvex=j; p->nextarc=G.vertices[i].firstarc; G.vertices[i].firstarc=p;} }// GreatAdjList
讨论:邻接表与邻接矩阵的比较 1. 联系:都可以用来存储有向图和无向图;邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数。 2. 区别: ①邻接矩阵是顺序结构,邻接表是链式结构 ②对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。 邻接矩阵的空间复杂度为O(n2),而邻接表的空间复杂度为O(n+e)。 邻接矩阵多用于稠密图的存储(e接近n(n-1)/2);而邻接表多用于稀疏图的存储(e<<n2)
三、 邻接多重表 (无向图的一种存储结构) • 在无向图的邻接表中,一条边出现两个单链表中.浪费空间,也给某种操作带来了困难。 • 在邻接多重表中,每一条边只有一个结点(称边结点)为有关边的处理提供了方便。
三、 邻接多重表 (无向图的一种存储结构) mark ivex jvex ilink jlink • 边结点的结构 Ebox: 其中: mark是记录是否处理过的标记; ivex和jvex是依附于该边的两顶点位置。 ilink域指向下一条依附于顶点ivex的边; jlink指向下一条依附于顶点jvex的边。 需要时还可设置一个存放与该边相关的权值的域cost。
三、 邻接多重表 (无向图的一种存储结构) data Firstarc 顶点结点的结构: • 其中: data存放与该顶点相关的信息; Firstarc 是指示第一条依附于该顶点的边的指针。 存储顶点信息的结点表以顺序表方式组织.在邻 接多重表中,所有依附于同一个顶点的边都链接 在同一个单链表中。从顶点 i出发, 可以循链找到 所有依附于该顶点的边,也可以找到它的所有邻 接顶点。 VexBox:
三、 邻接多重表 (无向图的一种存储结构) 4 1 2 4 2 1 2 3 0 3 0 1 0 a 例 b 1 a b 2 c c 3 d d e 4 e ^ ^ ^ ^ ^
四、十字链表(有向图的一种存储结构) • 可以把十字链表看成是将有向图的邻接表和逆 邻接表这两个表结合起来而得到的一种链表。 • 在十字链表中,有向图中每一条弧对应一个结点(称弧结点) • 每一个顶点也对应一个结点(称顶点结点)。