《数据结构》 第六章

本文详细介绍了图的概念,包括图的定义、基本术语,如子图、完全图、稀疏图等,并探讨了图的存储结构,如邻接矩阵、邻接表、十字链表和邻接多重表。接着,讨论了深度优先搜索和广度优先搜索这两种图的遍历算法,以及图的应用,如最小生成树的普拉姆算法和克鲁斯卡尔算法,最短路径的迪杰斯特拉算法,拓扑排序和关键路径的计算方法。
摘要由CSDN通过智能技术生成

第六章 图

6.1图的定义和基本术语

6.1.1 图的定义

:G由两个集合V和E组成,记为G=(V,E),其中V是顶点的有穷非空集合,E是V中顶点偶对的有穷集合,这些顶点偶对称为边;

6.1.2 图的基本术语

子图:子图的顶点集合和边集合都是图的顶点集合和边集合的子集;
无向完全图和有向完全图:对于无向图,若具有n(n-1)条边,则称为无向完全图;
对于有向图,若具有n(n-1)条弧,则称为有向完全图
稀疏图和稠密图:有很少条边或者弧的图称为稀疏图,反之称为稠密图;
权和网:每条边上具有某种含义的数值,该数值称为边上的权值,这种带权的图称为网;
临接点对于无向图G、,如果图的边(v,v1)属于E,则称顶点v和v1互为邻接点,即v和v1相邻接;边(v,v1)依附于顶点v和v1,或者说边(v,v1)与顶点相关联;
度,出度和入度:顶点v的度是指和v相关联的边的数目;入度是以顶点v为头的弧的数目;初读是以顶点v为尾的弧的数目;
路径和路径长度:再无向图G中,从顶点v到顶点v1的路径是一个顶点序列;路径长度是一条路径上经过的边或弧的数目;
回路或环:第一个顶点和最后一个顶点相同的路径称为回路或环;
简单路径,简单回路或简单环:序列中顶点不重复出现的路径称为简单路径;除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路或简单环;
连通,连通图和连通分量:在无向图G中,如果从顶点v到顶点v1有路径,则称其是连通的;若对于任意两个顶点且两个顶点都连通,那么称其所在的图为连通图;所谓连通分量就是无向图中的极大连通子图;(无向图中的子图为连通图)
强连通图和强连通分量:任意两个点都存在路径的连通图为强连通图;有向图中的极大强连通子图称作有向图的强连通分量;
连通图的生成树:一个极小连通子图,它含有图中的全部顶点,但只有足以构成一棵树的n-1条边,这样的连通子图称为连通图的生成树;(连通图中少了一条边构成了树);如果在一棵生成树中加入一条边必定构成一个环;
一个图可以有许多棵不同的生成树
所有生成树具有以下共同特点:

  • 生成树的顶点个数与图的顶点个数相同;
  • 生成树是图的极小连通子图,去掉一条边却非连通;
  • 一个有b条边的连通图的生成树只有n-1条边;
  • 生成树中再加一条边必然形成回路;
  • 生成树中任意两个顶点的路径是唯一的;
    有向树和生成森林:有一个顶点入度为0,其余顶点的入度均为1的有向图为有向树;一个有向图的生成森林是由若干棵有向树组成,含有图中的所有顶点,但只有足以构成若干棵不相交的有向树的弧;

6.2 案例引入

6.3 图的类型定义

顶点集:具有相同特性的数据元素的集合;

6.4 图的存储结构

图没有顺序存储结构,但其可以借助二维数组来表示元素间的关系,即采用邻接矩阵表示法;
图的存储结构:邻接表,十字链表和邻接多重表;

6.4.1 邻接矩阵

1.邻接矩阵表示法

邻接矩阵:表示顶点之间相邻关系的矩阵;
无向图的邻接矩阵:

  • 无向图的邻接矩阵是对称的;
  • 顶点i的度=第i行(列)中1的个数;
    特点:完全图的邻接矩阵中,对角元素为0,其余为1;

有向图的邻接矩阵:

  • 有向图的邻接矩阵可能是不对称的;
  • 顶点的出度=第i行元素之和;
  • 顶点的入度=第i行元素之和;
  • 顶点的度=顶点的入度+出度;
2.采用邻接矩阵表示法创建无向网
  • 输入总顶点数和总边数;
  • 依次输入点的信息并将其存入顶点表中;
  • 初始化邻接矩阵,使每个权值初始化为极大值;
  • 构造邻接矩阵;依次输入每条边依附的顶点和其权值,确定每个顶点在图中的位置之后,使相应的边赋予相应的值,同时使其对称边赋予相同的权值;
    算法时间复杂度:O(max()n^2,n*e);
    若建立无向图:只需改变算法的两处:一是初始化邻接矩阵时,将边的权值初始化为0;二是构造邻接矩阵时,将权值w改为常量值1即可;
3.邻接矩阵表示法的优缺点

1.优点

  • 便于判断两个顶点之间是否有边;
  • 便于计算各个顶点的度;对于无向图,邻接矩阵第i行元素之和就是顶点的度;对于有向图,第i行元素之和就是顶点的出度,第i列元素之和就是顶点的入度;
    2.缺点
  • 不便于增加和删除顶点;
  • 不便于统计边的数目,需要查找邻接矩阵所有元素才能统计完成;
  • 空间复杂度高;

6.4.2 邻接表

1.邻接表示法

在这里插入图片描述

在这里插入图片描述
邻接表:是图的一种链式存储结构;

表头结点表:由所有表头结点以顺序存储结构的形式存储,以便可以随机访问任一顶点的边链表;表头结点包括:数据域和链域;
边表:由图中顶点间关系的n个边链表组成;边链表中边界点包括:邻接点域,数据域和链域;
有向图:邻接表的特点:

  • 顶点Vi的出度为第i个单链表中的结点个数;
  • 顶点Vi的入度为整个单链表中邻接点域值是i-1的结点个数;
    逆邻接表:
  • 顶点Vi的入度为第i个单链表中的结点个数;
  • 顶点Vi的出度为整个单链表中邻接点域值是i-1的结点个数;
    无向图:邻接表的特点:
  • 邻接表不唯一;
  • 若无向图中有n个顶点,e条边,则其邻接表需要n个头结点和2e个表结点;适宜存储稀疏图;
  • 无向图中顶点Vi的度为第i个单链表的结点数;
    (链式)顶点:
    按编号顺序将顶点数据存储在一维数组中,关联同一顶点的边(以顶点为尾的弧)用线性链表存储;
2.采用邻接表表示法创建无向图

在这里插入图片描述

  • 输入总定点数和总边数;
  • 依次输入点的信息存入顶点表中,使每个表头结点的指针域初始化为NULL;
  • 创建邻接表,依次输入每条边依附的两个顶点,确定这两个顶点的讯号i和j,将此边结点分别插入Vi和Vj对应的两个边链表的头部;
    时间复杂度:O(n*e);
3.邻接表表示法的优缺点
1.优点
  • 便于增加和删除顶点;
  • 便于统计边的数目,按顶点表顺序查找所有边表可得到的数目,时间复杂度为O(n+e);
  • 空间效率高;
2.缺点
  • 不便于判断顶点之间是否有边,要判定Vi和Vj之间是否有边,就需查找第i个边表,最坏的情况下时间复杂度为O(n);
  • 不便于计算有向图哥哥顶点的度;
邻接表表示法与邻接矩阵的关系

1.联系:邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数;
2.区别:对于任一确定的无向图,邻接矩阵使唯一的(行列号与顶点编号一致),但邻接表不唯一,邻接矩阵的空间复杂度为O(n^2)而邻接表的空间复杂度为O(n+e);
3.用途:邻接表或逆邻接表表示的空间复杂度为O(n+e),适合表示稀疏图,对于稠密图,考虑到邻接表中要附加链域,因此常采用邻接矩阵表示法;

6.4.3 十字链表

十字链表:有向图的另一种链式存储结构,可以看成将有向图的邻接表和逆邻接表结合起来得到的一种链表;

6.4.4 邻接多重表

邻接多重表:无向图的另一种链式存储结构;
边结点:

mark 标志域:标记此边是否被搜索过ivex 该边依附的两个顶点在表头数组中位置ilink 边结点jvex同ilinkjlink 指向依附于jvex的下一条边info

6.5 图的遍历

图的遍历也是从图中某一顶点出发,按照某种方法对图中所有顶点进行访问且仅访问一次;
实质:找每一个顶点的邻接点的过程;
特点:因为图的任一顶点都可能和其余顶点相邻接,所以在访问了某个顶点之后,可能沿着某条路径搜索之后又回到该顶点上;
遍历图的路径:广度优先搜索(BFS)深度优先搜索(BFS)

6.5.1 深度优先搜索

1.深度优先搜索遍历过程

深度优先搜索遍历类似于树的先序遍历,使树的先序遍历的推广;

  1. 图中某个顶点出发,访问该顶点;
  2. 以刚访问过的顶点的下一个邻接点为顶点进行访问,直到没有未被访问过的邻接点为止;
  3. 返回前一个访问过且还有未访问的邻接点的顶点并重复2操作;
2.深度优先搜索遍历的算法实现
  • 从图中某个顶点出发,访问v,并置visited[v]的值为true;
  • 依次检查v的所有邻接点w,如果visited[w]的值为false,再从w出发进行递归遍历,直到图中所有顶点都被访问过;
3.深度优先搜索遍历的算法分析

当用邻接矩阵表示图时,查找每个顶点的邻接点的时间复杂度为O(n^2),其中n为顶点数;
当用邻接表作为图的存储结构时,查找邻接点的时间复杂度为O(e),其中e为图中的边数;
因此,当用邻接表作为存储结构使,深度优先搜索图的时间复杂度为O(n+e);
结论:稠密图适于在邻接矩阵上进行深度遍历
稀疏图适于在邻接表上进行深度遍历;

6.5.2 广度优先搜索

1.广度优先搜索遍历的过程

广度优先搜索遍历类似于树的按层次遍历的过程;

  • 从图中某个顶点出发开始访问;
  • 访问顶点的各个未被访问的邻接点;
  • 分别从这些邻接点出发依次访问它们的邻接点,使得前面被访问顶点的邻接点比后面被访问顶点的邻接点更早被访问,直到访问结束;
2.广度优先搜索遍历的算法实现

特点:尽可能先对横向进行搜索;

  • 从图中某个顶点v出发,访问v,并置visited[v]的值为true,然后使v入队;
  • 只要队列不空,则重复以下操作:
  1. 队头元素u出队;
  2. 依次检查u的所有邻接点w,如果visited[w]的值为false,则访问w,并置xisited[w]的值为true,然后使w入队;
3.广度优先搜索遍历的算法实现

当用邻接矩阵存储时,时间复杂度为O(n^2);
当用邻接表存储时,时间复杂度为O(n+e);
DFS和BFS的算法比较
空间复杂度相同,都是O(n)(借用了堆栈或队列)
DFS对栈的使用是在每次递归调用时对ip和实参进行自动压栈操作,是系统执行的,而BFS是手动入队;
时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关;

6.6 图的应用

6.6.1 最小生成树

生成树:所有顶点均由边连接一起,但不存在回路;
连通网的最小代价生成树:各边代价之和最小的那棵树(各边权值最下);简称最小生成树;
给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那颗生成树;
最合理的通信网应该是代价之和最小的生成树;

1.普拉姆算法

(找边的权值的最小值)
从最小边的权值开始,依次寻找该边所依附的结点的各条边的最小权值,直到所有结点被连接完且没有形成环;(有秩序)
注意:每次选择最小边时,可能存在多条同样权值的边可选,此时任选其一即可;

  • 首先将初始顶点u加入U中,对其余的每一个顶点Vj,将closedge[j]均初始化为u的边信息;
  • 循环n-1此,做如下处理:
  1. 从各组边closedge中选出最小边closedge[k],输出此边;
  2. 将k加入U中;
  3. 更新剩余的每组最小边信息closedge[j],对于V-U中的边,新增加了一条从k到j的边,如果新边的权值比closedge[j].lowcost小,则将closedge[j].lowcost更新为新边的权值;
2.克鲁斯卡尔算法

(找边的权值的最小值)
从最小边开始依次选择第二小第三小…的边直到连接完所有的结点且没有形成环;(无秩序)

  • 将数组Edge中的元素按权值从小到大排序;
  • 依次查看数组Edge中的边,循环执行以下操作:
  1. 依次从排好序的数组Edge中选出一条边
  2. 在Vexset中分别查找v1,v2所在的连通分量vs1和vs2进行判断:
    如果不等,表明所选的两个顶点分属不同的连通分量,输出此边,并合并vs1和vs2两个连通分量;
    如果相等,表明所选的两个顶点术语同一个连通分量,舍去此边而选择下一条权值最小的边;

6.6.2 最短路径

在这里插入图片描述

1.从某个源点到其余各顶点的最短路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XOjeVcvA-1680598816808)(20201110225457782.jpg)]
迪杰斯特拉算法:一个按路径长度递增的次序产生最短路径的算法;
本质:用三个数组来描述源点到各顶点的最短路径;

  • 用带权的邻接矩阵来表示带权的有向网,源点为V0,若无路径则用“无穷”符号表示;
  • 一维数组S来判断从源点到终点是否为最短路径,若是为true,若不是为false;
  • 一维数组Path记录从源点V0到终点Vi的当前最短路径上终点的直接前驱顶点序号;如果有弧,则赋予为v0,如果无弧,则为-1;
  • 一维数组D记录从源点到终点的当前最短路径长度,如果有弧,则赋予其权值,如果无弧,则为-1;
    算法实现
  1. 初始化:
  • 将源点V0加到S中;
  • 将V0到各个终点的最短路径长度初始化为权值;D[i]=G.arcs[V0][Vi](有向网的邻接矩阵)
  • 如果V0和顶点Vi上有弧,则将Vi的前驱置为V0(path[i]=V0),否则为-1;
  1. 循环n-1次,执行以下操作:
  • 选择下一条最短路径的终点;
  • 将该终点加入S中;D[k]=min{D[i]}
  • 根据条件更新从V0出发到任一顶点的最短路径的长度,若条件D[k]+G.arcs[k][i]<D[i]成立,则更新D[i]=D[k]+G.arcs[k][i],同时更改Vi的直接前驱为Vk,path[i]=K;
    弗罗伊算法
    辅助的数据结构:
  • 二维数组Path[i][j]:最短路径上顶点Vj的前一顶点序号;
  • 二维数组D[i][j]:记录顶点Vi和Vj之间的最短路径长度;
    算法步骤:
    将Vi到Vj的最短路径长度初始化,即D[i][j]=G.arcs[i][j],然后进行n次比较和更新;
  1. 在两顶点之间加入顶点V0,比较(Vi,Vj)和(Vi,V0,Vj)的路径长度,取其中较短者作为Vi到Vj的中间顶点序号不大于0的最短路径;
  2. 不断从两个顶点见加入新顶点,然后进行比较其最短路径与之前所获得的最短路径的长度,经过n次比较后,最后求得的必然是最短路径;

6.6.3 拓扑排序

1.AOV-网

无环的有向图称作有向无环图,简称DAG图;
AOV-网:用顶点表示活动,用弧表示活动间的优先关系的有向网称为以顶点表示活动的网;
特点:

  1. 从顶点Vi到顶点Vj有一条有向路径,则Vi是Vj的前驱,Vj是Vi的后继;
  2. 若<Vi,Vj>是网中的一条弧,则Vi是Vj的直接前驱,Vj是Vi的直接后继;
  3. 在AOV-网中,不应该出现有向环,因为存在环意味着某项活动以自己为先决条件;
    拓扑排序:将AOv—网中所有顶点排成一个线性排列,该序列满足:若在AOV-网中,从顶点Vi到顶点Vj有一条路径,则该线性序列中的顶点Vi必定在顶点Vj之前;
    检测AOV-网中是否存在环:对有向网构造其顶点的拓扑有序序列,若王忠所有顶点都在它的拓扑序列中,则该AOV-网必定不存在环;
2.拓扑排序的过程
  • 在有向图中选一个无前驱的顶点且输出它;
  • 从图中删除该顶点和所有以它为尾的弧;
  • 重复以上操作直至不存在无前驱的顶点;
  • 若此时输出的顶点数小于有向图的顶点数,则说明有向图存在环,否则输出的顶点序列即一个拓扑序列;
3.拓扑排序的实现

一维数组indegree[i]:存放各顶点的入度,没有前驱的顶点就是入度为0的顶点;

  1. 求出各顶点的入度并存入数组indegree[i]中,使入度为0的顶点入栈;
  2. 只要栈不空,则重复以下操作:
  • 使栈顶顶点Vi出栈并保存在拓扑序列数组topo中;
  • 对顶点Vi的每个邻接点Vk的入度减1,如果Vk的入度变为0,则使Vk入栈;
  1. 如果输出顶点个数少于AOV-网的顶点个数,则网中存在有向环,无法进行拓扑排序,否则拓扑排序成功;

6.6.4 关键路径

1.AOE-网

AOE-网:带权的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间;
源点:网中只要一个入度为0的点;
汇点:只有一个出度为0的点;
带权路径长度:一条路径各弧上的权值之和;(简称路径长度)
关键路径:带权路径长度最长的路径;
关键活动:关键路径上的活动;
确定关键路径所需定义4个描述量:

  1. 事件vi的最早发生时间ve(i):进入事件vi的每一活动都结束,vi才可发生,所以ve(i)是从原点到vi的最长路径长度;
  2. 事件vi的最迟发生事件vl(i):事件vi的发生不得延误vi每一个后继事件的最迟发生时间;
  3. 活动ai<Vj,Vk>的最早开始时间e(i):只有事件V(j)发生了活动ai才能开始;
  4. 活动ai<Vj,Vk>的最晚开始时间l(i):活动ai的开始事件需保证不延误事件vk的最迟发生时间;
2.关键路径求解的过程
  1. 对图中顶点进行排序,在排序过程中按拓扑序列求出每个事件的最早发生事件ve(i);
  2. 按逆拓扑序列求出每个事件的最迟发生时间vl(i);
  3. 求出每个活动ai的最早开始时间e(i);
  4. 求出每个活动ai的最晚开始时间l(i);
  5. 找出e(i) = l(i)的活动ai,即关键活动;
3.关键路径算法的实现
  1. 一维数组ve[i]:事件Vi的最早发生时间;
  2. 一维数组vl[i]: 事件Vi的最晚发生时间;
  3. 一维数组topo[i]:记录拓扑序列的顶点序号;
    算法描述:
  • 调用拓扑排序算法,使拓扑序列保存在topo中;
  • 将每个事件的最早发生时间ve[i]初始化为0,即ve[i]=0;
  • 根据topo中的值,按从前向后的拓扑次序,依次求每个事件的最早发生时间,循环几次,执行以下操作:
  1. 取得拓扑序列中的顶点序号k,k=topo[i];
  2. 用指针p依次指向k的每个邻接顶点,取得每个邻接顶点的序号j = p->adjvex,依次更新顶点j的最早发生时间ve[j];
  • 将每个事件的最迟发生时间v[i]初始化为汇点的最早发生时间,即v[i] = ve[n-1];
  • 根据topo中的值,按从后向前的逆拓扑次序,依次求每个事件的最迟发生时间,循环n次,执行以下操作:
  1. 取得拓扑序列中的顶点序号k,k=topo[i];
  2. 用指针p依次指向l的每个邻接顶点,取得每个邻接顶点的序号j=p->adjex,依次根据k的邻接点,更新k的最迟发生时间vl[k];
  • 判断某一活动是否为关键活动,循环n次,执行以下操作:对于每个顶点Vi,用指针p依次指向Vi的每个邻接顶点,取得每个邻接顶点的序号j=p->adjex,分别计算活动<Vi,Vj>的最早和最迟开始时间e和l,如果e和l相等,则活动<Vi,Vj>为关键活动,输出弧<Vi,Vj>;

6.7 案例分析与实现

6.8 小结

6.7 案例分析与实现

6.8 小结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值