第九章 解决图的编程问题

第九章      解决图的编程问题

图的定义:

图是由一系列定点(结点)和描述定点之间的关系边(弧)组成。图是数据元素的集合,这些数据元素被相互连接以形成网络。其形式化定义为:

G=(V,E)

V={(Vi|Vi∈某个数据元素集合)}

E={(Vi,Vj)|Vi+Vj∈V^P(Vi,Vj)}

其中,G表示图;V是顶点集合;E是边或弧的集合。在集合E中,P(Vi,Vj)表示顶点Vi和顶点Vj之间有边或弧相连

相关术语

顶点集:图中具有相同特性的数据元素的集合成为顶点集

边(弧):边是一对顶点间的路径,通常带箭头的边称为弧

弧头:每条箭头线的头顶点表示构成弧的有序对中的后一个顶点,称为弧头或终点

弧尾:每条箭头线的尾顶点表示成弧的有序对中的前一个顶点,称为弧尾或始点

度:在无向图中的顶点的度是指与那个顶点相连的边的数量

入度:顶点的入度是指向那个顶点的边的数量

出度:顶点的出度是由那个顶点出发的边的数量

权:有些图的边(弧)附带一些数据信息,这些数据信息称为边(或弧)的权

图的分类

有向图,无向图,有向完全图,无向完全图,稠密图,稀疏图

邻接矩阵

一种通用的图的存储结构,是由两个数组来表示图,一个数组是一维数组,存储图中的顶点信息,一个数组是二维数组,即矩阵,存储顶点之间相邻的信息,也就是边(弧)的信息

邻接表

邻接表的存储方法是一种顺序存储与链式存储相结合的存储方法,顺序存储部分用来保存图中顶点信息,链式存储部分用来保存图中边(或弧)的信息。具体的做法是:对于图中的顶点,使用一个一维数组,其中每个数组元素包含两个域,其结构为:

其中,

顶点域(data):存放与顶点有关的信息

头指针域(firstadj):存放与该结点相邻接的所有顶点组成的单链表的头指针。

邻接单链表中每个结点表示依附于该顶点的一条边,称为边结点,边结点结构为:

其中,

邻接点域(adjvex):指示与顶点邻接点在图中的位置,对应着一维数组中的序号,对于有向图,存放的是该边结点所表示的弧的弧头顶点在一维数组中的序号

数据域(info):存储边或弧相关的信息,如权值等

链域(nextadj):指向依附于该顶点的下一个边结点的指针

图的遍历

  1. 深度优先搜索算法:

从图的某一顶点出发,访问x,然后遍历任何一个与x相邻的违背访问的顶点y,再遍历任何一个与y相邻的未被访问的顶点z……如此下去,直到到达一个所有邻接点都被访问的顶点为止。然后依次退回到尚有邻接点未被访问过的顶点,重复上述过程,直到图中的全部顶点都被访问过为止。

深度优先搜索背后基于堆栈。

深度优先搜索,对下图所示的示例:

1)  将起点V1压入栈

2)  将顶点元素V1弹出栈,访问它,将与V1相邻的未被访问的所有顶点元素V4和V2压入栈

3)  从栈中弹出顶上的元素V2,访问它。将与V2相邻的未被访问所有顶点元素V6和V3压入栈

4)  从栈中弹出顶层元素V2,访问它。将与V3相邻的所有未被访问的元素V6压入栈

5)  从栈中弹出顶层元素V5,访问它。在栈中压入所有它的未访问的临接顶点,点V5没有任何未被访问的邻接点,因此没有顶点被入栈中

6)  从栈中弹出顶层元素V6访问它。在栈中压入所有它的未访问的邻接点,顶点V6没有任何被访问的邻接点,因此没有顶点被入栈中

7)  从栈中弹出顶层元素V4,访问它。在栈中压入所有它的未被访问的邻接顶点。顶点V4没有任何未被访问的邻接顶点,因此没有顶点被入栈中,在V4弹出后,栈中的内容是空的,因此,遍历完成

  1. 广度优先搜索

图的广度优先搜索是从图的某个顶点x出发,访问x。然后访问与x相邻接的所有未被访问的顶点x1、x2……xn相邻的未被访问过的所有顶点。以此类推,直至图中的每个顶点都被访问。

对示例进行广度优先搜索如下:

1)  从第一个顶点V1开始遍历。在访问了顶点V1之后,访问与顶点V1邻接的所有顶点。与V1邻接的顶点有V2和V4.可以以任何顺序访问顶点V2和V4,假设先访问顶点V2,再访问顶点V4

2)  先遍历与V2邻接的所有未被访问的顶点,与V2邻接的未被访问的顶点是V3和V6,先访问V3再访问V6;然后访问与V4邻接的顶点,与V4邻接的未被访问的顶点是V5

3)  依次遍历与顶点V3、V6和V5邻接的未被访问的顶点,没有与V3、V6和V5相邻接的未被访问的顶点所有的顶点都被遍历了

图的最短路径(Dijkstra算法)

基本思想:设置两个顶点集合T和S,集合S中存放已经找到最短路径的顶点,集合T中存放当前还未找到最短路径的顶点。初始状态时,集合S中只包含源点v0,然后不断从集合T中选取到源点v0路径最短的顶点w加入集合S,集合S中每加入一个新的结点w,都要修改顶点v0到集合T中剩余顶点的最短路径长度值,集合T中各顶点新的最短路径长度值为原来最短路径长度值与顶点w的最短路径长度加上w到该顶点的路径长度值中的较小值。此过程不断重复,直到集合T的顶点全部加入到集合S中为止。

下面用下图给出Dijstra算法的具体实现

采用邻接矩阵作为存储结构:

Vij  顶点i和j之间的权值

matrix[i,j]= 0   i=j顶点i和j是同一个顶点

∞  顶点i和j之间没有边

设置一个一维数组final来标记已找到最短路径的顶点,并规定:

除了final数组外,还需要另一个数组distance,它用来存储从A到其他城市的距离。距离可以使直接的也可以间接的

如果从城市A出发,对于上图,数组distance和final将被初始化为:

distance={0,90,∞,150,∞,180}

final={1,0,0,0,0,0}

下面详细描述Dijkstra算法来确定从城市A到其他城市的最短距离。步骤如下:

1.选择数组distance中具有最短路径的顶点v,使得:

distance[v]=min{distance(w)}(s[w]=0)

然后将v加入集合final中,即令final[w]=1

在distance数组中具有最小距离的顶点A(距离是0)。但该结点已经在final数组中标记为1,因此,选择对应于下一个最小距离的顶点,也就是顶点B,距离是90,并将其对应的final值设为1,如图1所示:

2.对于所有final[i]=0的顶点wi,判断distance[i]是否小于distance[v]+matrix[v,i],如果不是,则使得:

distance[i]= distance(i)}+matrix[v,i]

这里,v=1,并且distance[1]=90,现在考虑所有不在final中的顶点

  • w=2:从A途径B到C的距离是:90+60=150,它小于已经记录的距离distance[2](∞),因此distance[2]改为150
  • w=3:从A途径B到D距离的距离是:90+50=140,它小于已经记录的距离150,因此distance[3]改为140
  • w=4:从A途径B到到E的距离是:90+∞=∞,distance[4]保持不变
  • w=5:从A途径B到F的距离是:90+∞=∞,它大于已经记录的距离180,distance[5]保持不变

如图2所示

  1. 选择数组distance中具有最短路径的顶点v,使得:

distance[v]=min{distance(w)}(s[w]=0)

然后将v加入到集合final中,即令final[w]=1

在distance数组中没有在final数组中标记为1的具有最小距离的顶点是D距离是140,并将其对应的final设为1,如图3所示

4. 对于所有final[i]=0的顶点wi,判断distance[i]是否小于distance[v]+matrix[v,i],如果不是,则使得:

distance[i]= distance(i)}+matrix[v,i]

这里,v=3,并且distance[3]=140,现在考虑所有不在final中的顶点

  • w=2:从A途径D到C的距离是:90+∞=∞,因此distance[2]不变
  • w=4:从A途径D到到E的距离是:140+110=250,它小于已经记录的距离∞因此distance[4]改为250
  • w=5:从A途径D到F的距离是:90+280=420,它大于已经记录的距离180,distance[5]保持不变

如图4所示

5 选择数组distance中具有最短路径的顶点v,使得:

distance[v]=min{distance(w)}(s[w]=0)

然后将v加入到集合final中,即令final[w]=1

在distance数组中没有在final数组中标记为1的具有最小距离的顶点是C距离是150,并将其对应的final设为1,如图5所示

6. 对于所有final[i]=0的顶点wi,判断distance[i]是否小于distance[v]+matrix[v,i],如果不是,则使得:

distance[i]= distance(i)}+matrix[v,i]

这里,v=2,并且distance[3]=140,现在考虑所有不在final中的顶点

  • w=4:从A途径C到到E的距离是:150+230=380,它大于已经记录的距离250,distance[4]保持不变
  • w=5:从A途径C到F的距离是:150+80=230,它大于已经记录的距离180,distance[5]保持不变

如图6所示

7. 选择数组distance中具有最短路径的顶点v,使得:

distance[v]=min{distance(w)}(s[w]=0)

然后将v加入到集合final中,即令final[w]=1

在distance数组中没有在final数组中标记为1的具有最小距离的顶点是F 距离是180,并将其对应的final设为1,如图7所示

8. 对于所有final[i]=0的顶点wi,判断distance[i]是否小于distance[v]+matrix[v,i],如果不是,则使得:

distance[i]= distance(i)}+matrix[v,i]

这里,v=5,并且distance[5]=180,现在考虑所有不在final中的顶点

  • w=4:从A途径F到到E的距离是:180+30=210,小于已经记录的距离250因此distance[4]改为210

如图8所示

9. 选择数组distance中具有最短路径的顶点v,使得:

distance[v]=min{distance(w)}(s[w]=0)

然后将v加入到集合final中,即令final[w]=1

在distance数组中没有在final数组中标记为1的具有最小距离的顶点是E距离是210,并将其对应的final设为1

现在所有顶点都在final数组中被标记为1.这样distance数组存放的就是从源A到其他顶点的最短路径,如图9所示

时间: 2024-10-12 10:58:17

第九章 解决图的编程问题的相关文章

第三章解决堆栈的编程问题

第三章      解决堆栈的编程问题 堆栈是一种特殊的线性表,是一种只允许在表的一端进行插入或删除操作的线性表. 堆栈的主要特点是后进先出. 用一片连续的存储空间来存储栈中的元素,这样的栈称为顺序栈. 用链式存储结构存储的栈称为链栈. 汉诺塔问题 汉诺塔问题来自一个古老的传说:在世界刚刚被创建的时候有一座砖石宝塔(A),其上有64个金蝶.所有按从大到小的顺序从塔底堆放至塔顶.紧挨着这座塔有两个砖石塔(B和C).从世界创始之日起,婆罗门的牧师们就一直在试图把A塔上的碟子移动到B上去,其间借助于C的

第七章解决二叉树的编程问题

第七章      解决二叉树的编程问题 二叉树是n(≥0)个有限元素的集合,该集合或者为空,或者由一个称为根的元素及两个不相交的,被称为左子树和右子树的二叉树组成.当集合为空时,称该二叉树为空二叉树,在二叉树中一个元素也称为一个结点. 二叉树是有序的,即将其左右子树颠倒,就成为另一个不同的二叉树. 结点的度,结点所拥有的子树的个数 叶结点,度为0的结点,也称为终端结点 分支节点,度不为0的结点,也称为非终端结点 孩子.兄弟.双亲结点,树中一个结点的子树的根节点称为这个结点的孩子.这个结点称为孩子

第九章、shell脚本编程基础

第九章.shell脚本编程基础 本章内容 编程基础 脚本基本格式 变量 运算 条件测试 配置用户环境 编程基础 程序:指令+数据 程序编程风格: 过程式:以指令为中心,数据服务于指令 对象式:以数据为中心,指令服务于数据 shell程序:提供了编程能力,解释执行 程序的执行方式 计算机:运行二进制指令 编程语言: 低级:汇编 高级: 编译:高级语言-->编译器-->目标代码 java,C# 解释:高级语言-->解释器-->机器代码 shell, perl, python (系统后台

第四、五章解决队列和串的编程问题

第四章      解决队列的编程问题 队列是一种特殊的线性表,是一种只允许在表的一端进行插入操作而在另一端进行删除操作的线性表.把进行插入操作的表尾称为队尾,进行删除操作的头部称为对头: 队列的主要特点是:先进先出,或后进后出 用一片连续的存储空间来存储队列中的数据元素,这样的队列称为顺序队列. 将顺序队列看成是首位相接的循环结构,这种队列叫做循环队列. 第五章      解决串的编程问题 串即字符串,是由0个或多个字符组成的有限序列,是数据元素为单个字符的特殊线性表. 顺序结构存储串: 串的静

c++ primer plus(第6版)中文版 第九章编程练习答案

首先,说明下环境: linux:fedora14: IDE:eclipse: python:python2.7 python框架:django web服务器:apache web服务器的python模块:mod_wsgi 写在前面: 之前用的windows下面的xampp,写的php后台,现在想转向linux下面的python,跟以前一样,选择apache和eclipse作为自己的开发工具. eclipse的python配置, 参见之前的博客:http://blog.csdn.net/zy416

C Primer Plus (第五版) 第九章 函数 编程练习

第九章 函数 编程练习 设计函数 min(x,y),返回两个double数值中较小的数值,同时用一个简单的驱动程序测试该函数. #include <stdio.h> double min(double x, double y); int main(void) { double a, b; printf("请输入两个数:\n"); scanf("%lf%lf", &a, &b); printf("两个数中较小的数是:%lf\n&q

JavaScript DOM编程艺术-学习笔记(第八章、第九章)

第八章 1.小知识点: ①某些浏览器要根据DOCTYPE 来决定页面的呈现模式(标准模式 / 怪异模式--也称兼容模式): 兼容模式意味着浏览器要模仿老一辈的浏览器的怪异行为,来让老站点得到运行,并让不规范的页面得到运行. 使用时应避免出发兼容模式. html5 DOCTYPE默认的是标准模式 ②abbr标签--简称.缩写.此标签到ie7才被ie支持 2.js只能充实文档内容,避免使用dom技术来创建核心内容.    避免使用dom设置重要样式 第九章 1.css正在利用伪类(例如:hover.

Java编程思想---第九章 接口(上)

第九章 接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 9.1 抽象类和抽象方法 Java提供一个叫做抽象方法的机制,这个机制是不完整的,仅有声明而没有方法体,抽象方法的语法如下: abstract void f(); 包含抽象方法的类叫做抽象类,如果一个类包含一个或者多个抽象方法,该类必须被限定为抽象的,否则编译器就会报错. 如果一个抽象类不完整,那么当我们试图产生该类的对象时,由于抽象类创建对象时不安全的,所以我们会从编译器那里得到一条出错消息,这样编译器会确保抽象类

Laxcus大数据管理系统2.0(11)- 第九章 容错

第九章 容错 在当前,由于集群庞大的组织体系和复杂性,以及用户普遍要求低成本硬件,使得集群在运行过程中发生的错误概率,远远高于单一且性能稳定的小型机服务器,并且集群在运行过程中几乎是不允许停止的,这就更需要提供比单机环境复杂得多的错误管理方案.实际上,我们在产品设计.开发.运营的各个阶段,有相当大一部分精力,都是用来获取各种故障,和解决各种故障发生后的错误处理问题.对于这些错误处理,我们整体遵循这样一个思路来解决:首先由软件感知来发现和定位故障点,然后进行判断,如果属于软件可以解决的故障,那么通