550 likes | 661 Views
Chap7. 图及图的应用. 第 7 章 图及图的应用. 7.1 图的基本概念. 7.1.1 图的定义. 1 .定义 图( Graph ) 是由非空的顶点集合和一个描述顶点之间的关系 —— 边(或者弧)的集合组成,其形式化定义为: G= ( V , E ) V={vi | vi∈V } E={( vi,vj )| vi,vj∈V ∧P( vi,vj )}
E N D
Chap7 图及图的应用
7.1图的基本概念 7.1.1 图的定义 1.定义 图(Graph)是由非空的顶点集合和一个描述顶点之间的关系——边(或者弧)的集合组成,其形式化定义为: G=(V,E) V={vi |vi∈V} E={(vi,vj)|vi,vj∈V ∧P(vi,vj)} 其中,G表示一个图,V是图G中顶点的集合,E是图G中边(或者弧)的集合,集合E中P(vi,vj)表示顶点vi和顶点vj之间由一条直接连线,即偶对(vi,vj)表示一条边。
7.1图的基本概念 7.1.2 图的相关术语 • (1)无向图 • 在图G中,如果代表边的结点偶对是无序的,则称G为无向图。把无向图中的无序偶对用圆括号括起来,以表示一条无向边。 • 图7.1 图的示例
7.1图的基本概念 7.1.2 图的相关术语 (2)有向图 在图G中,如果表示边的结点偶对是有序的,则称图G为有向图。把有向图中的有序偶对用尖括号括起来,用以表示一条有向边,有向图中的一条边又称作弧。 • 图7.1 图的示例
7.1图的基本概念 7.1.2 图的相关术语 (3)完全图 在一个具有n 个结点的无向图中,若每一个结点与其他n-1个结点之间都有边的联系,则有n(n-1)/2条边,这是任何具有n个结点的无向图所具有的最大边数。 一个具有n(n-1)/2条边和n个结点的无向图称作无向完全图。例如,图7.1中G1是具有4个结点的无向完全图。 类似地,在一个具有n个结点的有向图中,最多有n(n-1)条有向边。具有n个结点和n(n-1)条有向边的图称作有向完全图。
7.1图的基本概念 7.1.2 图的相关术语 (4)网 在一些实际应用中,图的边往往和具有一定意义的数相关。这种与图的边相关的数叫权,这个权可表示从一个结点到另一个结点的距离或花费的代价等,我们把边具有权的图称为网。图7.2给出了网的示例。图中结点v1到v3所表示边的权是5,v2到v4所表示边的权是4。 图7.2 网的示例
7.1图的基本概念 7.1.2 图的相关术语 (5)子图 设有图G和G',如果满足下列条件: V(G')V(G) E(G')E(G)则称图G‘是G的子图。
7.1图的基本概念 7.1.2 图的相关术语 (6)邻接 若(vi,vj)是E(G)中的一条边,则称结点vi和vj是邻接的结点,而称边(vi,vj)是依附于结点vi,vj的边。 (7) 度、入度、出度 1) 度 依附于结点的边的数目称为结点的度。 2) 入度和出度 在有向图中,结点的度可分为入度、出度和总度。我们把以结点v开始的有向边的数目称为v结点的出度;而把有向边射到结点v的数目称为v结点的入度。结点出度和入度的总和就是该结点的总度,也简称为结点的度。
7.1图的基本概念 7.1.2 图的相关术语 (8)路径、路径长度、回路 1) 路径 在图G中,从结点vp到vq的路径是结点序列(vp,vi1,vi2,…,vin,vq),且(vp,vi1),(vi1,vi2),…,(vin,vq)是E(G)中的边。若G是有向图,则路径也是有向的,由有向边<vp,vi1>,<vi1,vi2>,…,<vin,vq>组成。 2) 路径长度 在一条路径中,除去第一个结点和最后一个结点之外,其余结点都各不相同,则称这样的路径为简单路径。
7.1图的基本概念 7.1.2 图的相关术语 3) 回路 我们把第一个结点和最后一个结点相同的路径称为回路。若一条简单路径的第一个结点和最末一个结点相同,其他结点均不相同,则称为简单回路。 (9)连通图、连通分量、强连通图、弱连通图 1) 连通图 在无向图中,若从结点vi到vj存在一条路径(由于G是无向图,因此vj到vi也存在一条路径),则vi和vj是连通的。 2) 连通分量 所谓连通分量,就是指无向图中的极大连通子图。
7.1图的基本概念 7.1.2 图的相关术语 3) 强连通图 在有向图G中,若V(G)中的任意两个不同的结点vi,vj之间,既存在一条vi到vj的路径,又存在一条vj到vi的路径,则称有向图G是强连通图。 4) 弱连通图 若把有向图看作无向图时是连通的,而该图作为有向图可以不是连通的,则称该图为弱连通图。
1 2
7.2图的存储结构 7.2.1 邻接矩阵 2.邻接矩阵的对称性 无向图的邻接矩阵是对称的,当且仅当(vi,vj)是E(G)中的一条边时,(vj,vi)也是E(G)中的一条边。这是由于无向图中的边是没有方向的,结点vi至vj可达,vj至vi也同时可达。在图G对应的邻接矩阵A中,若A(i,j)=1,则A(j,i)=1,表现为矩阵A对于主对角线是对称的。 而对于有向图来说,邻接矩阵则不一定是对称的。
7.2图的存储结构 7.2.1 邻接矩阵 3.存储空间 用邻接矩阵表示一个有n个结点的图G,需要有n2个元素。对无向图,由于其邻接矩阵是对称的,因此只需存储矩阵的上三角或下三角矩阵中的元素。故有n个结点的无向图所需的存储空间为n(n+1)/2个(实际上对角线可以不存储,因此只需n(n-1)/2个)
7.2图的存储结构 7.2.1 邻接矩阵 4.邻接矩阵的简单运算 根据图的邻接矩阵,可以判断V(G)中任意两个结点vi和vj之间是否有边相连。 通过邻接矩阵还可以方便地确定各顶点的度。就无向图而言,任意顶点vi的度就是第i行元素之和 对有向图,结点vi的出度是第i行元素之和,vj结点的入度是第i列元素之和。
7.2图的存储结构 7.2.1 邻接矩阵 5.网的邻接矩阵 设Wij是结点vi至vj的权,则 用邻接矩阵所表示的图,很容易实现计算机的存储和处理,通常用二维数组A(1:n,1:n)表示。
7.2图的存储结构 7.2.1 邻接矩阵
vertex next 7.2图的存储结构 7.2.2 邻接表和逆邻接表 图的链接存储主要有邻接表、逆邻接表。 1.邻接表 邻接表的结点结构 对于网,链结点结构中还可以增加一个存放权的域。
7.2图的存储结构 7.2.2 邻接表和逆邻接表 2) 邻接表的结构
7.2图的存储结构 7.2.2 邻接表和逆邻接表 3) 建立邻接表的算法 下面给出建立邻接表的算法。 vert=n+1; /* n是图G的结点个数,vert为1…n */ struct node { struct node*next; int vertex; } *adjlist[vert];
7.2图的存储结构 7.2.2 邻接表和逆邻接表 3) 建立邻接表的算法 /* 建立有n个结点的图的邻接表 */ READ/LIST(int n) { inti for(i=1; i<=n; i++) { p=malloc(sige); // 取一个新结点 scanf("%d",&x); // 输入结点的序号 p->vertex=x; adjlist[i]=p; // adjlist[i]是结点的线性入口表 scanf("%d",&m); // 输入与第i个结点相邻接的顶点个数 for(j=1; j<=m; j++) { q=malloc(sige); // 取一个新结点 scanf("%d",&x); // 输入与第i个结点相邻接的顺序结点序号 q->vertex=x; p->next=q; p=q;} p->next=NULL;}
7.2图的存储结构 7.2.2 邻接表和逆邻接表 4) 存储空间 对于一个有n个结点和e条边的无向图,若采用存储表示法,则需n个头结点和2e个表结点。每个结点有两个域(对于网有三个域)。显然,在较稀疏的情况下,采用邻接表比邻接矩阵能节省较多的存储空间。 5) 结点的度 在无向图的邻接表中,第i个链表中的结点个数就是结点vi的度。在有向图中,第i个链表中的结点个数是vi的出度,至于vi的入度必须查遍所有链,找出指向vi(即vertex(i))的个数,就是vi的入度。
7.2图的存储结构 7.2.2 邻接表和逆邻接表 2.逆邻接表
7.3图的遍历 根据搜索方法的不同,图的遍历方法有两种:一种叫做深度优先搜索法(DFS);另一种叫做广度优先搜索法(BFS)。 7.3.1深度优先搜索 深度优先搜索法(Depth First Search)简称DFS。 按深度优先搜索法对图中各结点进行遍历的过程是:首先以给定的任一结点vi为起始点,访问该结点,然后选取一个与给定结点vi相邻且未被访问的结点v1,注意v1是按在vi为入口的邻接表中以链指针为顺序,取VISITED(i)为零的未被访问的原则来选取的,以v1作为新的起始点,重复上述过程。当到达一个其所有相邻接的结点都已被访问过的结点vj时,就退回到最近被访问过的结点vj-1。
7.3图的遍历 7.3.1深度优先搜索 得到结点访问序列为 v1→v2→v4→v8→v5→v3→v6→v7
7.3图的遍历 7.3.1深度优先搜索 1.图的存储结构 遍历过程中需要寻找与v1结点相邻接的结点,因此采用邻接表的结构。 2.算法思想 在深度优先遍历过程中,若遇到某个结点的所有邻接表都被访问过,则需要返回到上一个被访问的结点。为得到上一个被访问的结点,需要设置一个栈保存每次被访问的结点。当一个结点被访问时,此结点进栈,而当某个结点的所有邻接点都被访问过时,再从栈中取出一个上一次被访问的结点。
7.3图的遍历 7.3.1深度优先搜索
7.3图的遍历 7.3.1深度优先搜索 3.深度优先搜索的算法 DFS(adilist, vi) { visited[vi]=1; // 置访问标志 printf("%d\n", vi); // 访问结点vi p=adjist[vi]; //取结点vi的链表表头 while(p!=NULL) { if(visited[vertex[p]]==0) DFS2(adjlist,vertex[p]); // 递归地从调用结点开始深度优先遍历 p=link[p]; } }
7.3图的遍历 7.3.2广度优先搜索 广度优先搜索法(Breath First Search)简称BFS。用广度优先搜索法遍历图G时,若从某一个结点v0开始,首先访问该结点v0,然后按v0的邻接表访问所有与v0邻接的结点v1,v2,…,vi,再依次与访问与v1,v2,… ,vi邻接的所有未被访问的结点。再从这些结点出发,逐次访问所有未被访问过的图中的结点,直到图中所有的结点被访问为止。这种图遍历的过程称作广度优先搜索法
7.3图的遍历 7.3.2广度优先搜索 广度优先搜索法(Breath First Search)简称BFS。用广度优先搜索法遍历图G时,若从某一个结点v0开始,首先访问该结点v0,然后按v0的邻接表访问所有与v0邻接的结点v1,v2,…,vi,再依次与访问与v1,v2,… ,vi邻接的所有未被访问的结点。再从这些结点出发,逐次访问所有未被访问过的图中的结点,直到图中所有的结点被访问为止。这种图遍历的过程称作广度优先搜索法
7.3图的遍历 7.3.2广度优先搜索 得到结点访问序列为 v1→v2→v3→v4→v5→v6→v7→v8
7.3图的遍历 7.3.2广度优先搜索 1.图的存储结构 遍历过程中需要寻找与vi结点相邻接的结点,因此仍然采用邻接表结构。 2.算法思想 广度优先搜索体现了按层次遍历(开始出发的结点为第一层,其邻接点为第二层,依次类推)的思想。在遍历过程中,当第i层的某个结点的所有邻接点都被访问过,则需要返回第i?1层的第一个被访问的结点。为此,需要设置一个队列来保存每次被访问的结点。当一个结点被访问时,该结点进队,而当某个结点的所有邻接点都被访问过时,从队列中取出一个结点,即第i-1层的第一个被访问的结点。
7.3图的遍历 7.3.2广度优先搜索
7.3图的遍历 7.3.2广度优先搜索 3.广度优先搜索的算法 /* 在具有n个结点的无向图中,以邻接表的形式表示,表头指针用一维数组adjlist表示,从结点vi开始,一维数组visited为访问标志位,初值为零,q为队列 */ BFS(adjlist,v0) { visited[vi]=1; printf("%d\n", vi); //访问vi ADDQ(vi, q); // 结点vi进队 while(q!=NULL) { DELETEQ(vi, q); // 从队中取出结点vi p=adjlist[vi]; // 取结点vi的链表表头 while(p!=NULL) { if(visited[vertex[p]]= =0){ visited[vertex[p]]=1; // 置访问标志 printf("%d\n", vertex[p]); // 访问VERTEX(p)结点 ADDQ(vertex[p], q); } p=link[p];} }}
7.3图的遍历 7.3.2广度优先搜索 4.算法分析结果 该算法的执行时间与深度优先搜索法相同,运行时间主要花费在while循环上。对结点的邻接表中每个结点至多检查一次。因表中有2e个结点,故完成遍历所需要的时间为O(e),e为图中的边数。
7.4图的应用 7.4.1最小生成树 1.最小生成树的概念 生成树: 对于连通图G=(V, E),E(G)是图G所有边的集合,V(G)是所有顶点的集合,若图G有n个顶点,V(G)所有的n个顶点和E(G)中n-1条边构成的图为图G的生成树。
7.4图的应用 7.4.1最小生成树
7.4图的应用 7.4.1最小生成树
7.4图的应用 7.4.1最小生成树 2.Prim算法 假设N=(V, E)是连通网,生成的最小生成树为T=(V, TE),求T的步骤如下: (1) 初始化:U={u0}, TE={ }。 (2) 在所有u∈U,v∈V-U的边(u, v)中,找一条权最小的边(u0, v0),TE+{(u0, v0)}TE, {v0}+UU。 (3) 如果U=V,则算法结束;否则重复(2)。 最后得到最小生成树T=<V,TE>,其中TE为最小生成树的边集。
7.4图的应用 7.4.1最小生成树 Prim算法的时间复杂度为O(n2)。
7.4图的应用 7.4.1最小生成树 3.Kruskal算法 构造最小生成树的步骤如下: (1) 置U的初值等于V(即包含有G中的全部顶点),TE的初值为空集(即图T中每一个顶点都构成一个分量)。 (2) 将图G中的边按权值从小到大的顺序依次选取:若选取的边未使生成树T形成回路,则加入TE;否则舍弃,直到TE中包含(n-1)条边为止。
7.4图的应用 7.4.1最小生成树 Kruskal算法的时间复杂度为O(n2)。
7.4图的应用 7.4.2最短路径 由于从一顶点到另一顶点可能存在着多条路径,每条路径上所经过的边数可能不同,即路径长度不同,我们把路径长度最短(即经过的边数最少)的那条路径叫做最短路径,其路径长度叫做最短路径长度或最短距离。 对于带权的图,考虑路径上各边上的权值,则通常把一条路径上所经边的权值之和定义为该路径的路径长度或称带权路径长度。 从源点到终点可能不止一条路径,把带权路径长度最短的那条路径称为最短路径,其路径长度(权值之和)称为最短路径长度或者最短距离。
7.4图的应用 7.4.2最短路径
7.4图的应用 7.4.2最短路径 从顶点V1到各顶点之间的最短路径
7.4图的应用 7.4.2最短路径 1.采用狄克斯特拉(Dijkstra)算法求解基本思想是: 设G=(V,E)是一个带权有向图, 把图中顶点集合V分成两组: 第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径v,…vk,就将vk加入到集合S中,直到全部顶点都加入到S中,算法就结束了) 第二组为其余未确定最短路径的顶点集合(用U表示)。
7.4图的应用 7.4.2最短路径 2.狄克斯特拉算法的具体步骤如下: (1) 初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,U中顶点u距离为边上的权(若v与u有边<v,u>)或∞(若u不是v的出边邻接点)。 (2) 从U中选取一个距离v最小的顶点k,把k加入S中(该选定的距离就是v到k的最短路径长度)。 (3) 以k为新考虑的中间点,修改U中各顶点的距离:若从源点v到顶点u(u∈U)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边<k,u>上的权。 (4) 重复步骤(2)和(3)直到所有顶点都包含在S中。
7.4图的应用 7.4.3 拓扑排序 拓扑排序是有向图的一个重要操作。在给定的有向图G中,若顶点序列vi1,vi2,...,vin满足下列条件:若在有向图G中从顶点vi到顶点vj有一条路径,则在序列中顶点vi必在顶点vj之前,便称这个序列为一个拓扑序列。求一个有向图拓扑序列的过程称为拓扑排序。
7.4图的应用 7.4.3 拓扑排序