1、I图的遍历动态演示程序摘要:图是一种复杂的数据结构,具有较高的学习难度。本文讲述了对图的动态演示程序的操作和程序的具体实现过程,使得我们对图的认识更深刻,学习更容易。本软件以 Visual Studio 2008 作为开发工具,使用邻接表法,用 MFC类库实现了对图的可视化创建和图的遍历的动态演示。本文首先讲解了图的遍历动态演示程序的实现框架和设计思路,然后深入讲解了对图中结点和弧的创建、插入和删除,最后着重讲解了图的深度优先遍历和广度优先遍历动态演示的具体实现。关键词:图; 遍历; 动态演示IIThe dynamic demonstrative program of traverse gra
2、phAbstract: Graph is a complex data structure, which is hard to learn. This thesis tells people the manipulate of the dynamic demonstrate of traverse graph and the specific realization progress of the program. This study give us a deeper understanding of graph, as well as make it easier to learn it.
3、 This software realizes the visual creation of graph and the dynamic demonstration of traverse graph by using adjacent table, MFC library and Visual Studio 2008. This thesis firstly explains the realization of the dynamic demonstrate of traverse graph program, the go into the depth of the creation,
4、insertion, deleting of node and arc, at last explains emphatically the actual realization of the Depth-First traverse of graph and the Breadth-First traverse of graph.Key Words:graph, traverse, dynamic demonstrativeIII目 录1 引言 11.1 开发背景 .11.2 开发的目的以及意义 .12 需求分析 12.1 功能概述 .12.2 功能需求分析 .22.2.1 结点的操作 .2
5、2.2.2 弧的操作 .22.2.3 自动生成图的支持 .22.2.4 支持图的销毁 .32.2.5 图的遍历类型 .32.2.6 图的存储结构 .32.2.7 图的遍历代码 .32.2.8 支持图的遍历次序显示和中间辅助队列的进出队情况显示 .32.2.9 支持对遍历速度的设置 .32.2.10 支持暂停和单步 .32.2.11 支持对图的实现代码的查看和运行 .42.2.12 支持对版本和帮助的显示 .43 总体设计 43.1 程序框架的搭建 .43.1.1 工程项目的创建 .43.1.2 窗口的显示 .43.2 菜单的制作 .63.2.1 创建图 .63.2.2 设置演示速度 .83.2
6、.3 查看源代码的实现 .83.2.4 运行此程序菜单的实现 .93.2.5 打开此文件菜单和帮助菜单的实现 .103.2.5 版本菜单的实现 .103.2.6 退出菜单功能的实现 .103.3 图的创建和遍历核心算法的设计与实现 103.3.1 算法的设计 .103.3.2 核心算法的实现 .164 测试与总结 28谢 辞 29参 考 文 献 3011 引言在纷繁复杂的社会生活中,很多东西都涉及到图的应用问题。最早的图的应用可以追溯到 18 世纪伟大的数学家欧拉用图解决了著名的哥尼斯堡桥问题。目前,图的应用已经渗透到诸如电子线路分析、寻找最短路径、工程计划分析、人工智能、信息检索等领域。而图
7、的遍历是运用图解决问题必须掌握的知识。1.1 开发背景社会生活中很多问题都涉及到“图”的知识,这些问题一般都比较复杂,比较难以解决,要解决这些问题,对“图”的学习是必须的。但目前对于“图”知识的讲解用得最多的是 ppt 演示,而 ppt 只能演示已经设计好的“图” ,灵活性差,而且这些 ppt 的制作过程都比较繁琐。因此,设计一个能创建动态图,并且可以演示其遍历的软件非常重要。本次毕业设计我用 MFC 开发一个图的遍历动态演示程序,希望能让初学者能够更好、更容易地掌握图的知识。1.2 开发的目的以及意义为了让初学者更轻松地掌握“图”的知识,有必要进行本次毕业设计。本次的毕业设计是将书本上所学的
8、理论知识与实际相结合,是对所学知识的一种检查,是对动手能力的一种锻炼,同时也是从学习走向真正开发的一个过渡,希望通过本次的毕业设计使自己在程序的开发和设计上有新的认识并能有所提高。2 需求分析2.1 功能概述首先是对图类型的选择,图的类型分为有向图和无向图。其次是图的创建和销毁。图的创建包括对图结点和弧的添加、插入和删除,其中添加和插入可以和成一个功能,都是增加数据信息。这些功能的实现还必须在图形界面上显示出来,且用2户能够方便地进行操作。最后就是图的遍历。图的遍历分为深度优先遍历和广度优先遍历,选择不同的遍历方式运行时,根据代码的执行步骤及时的在界面上反应遍历到哪个结点和图存储结构上的动态显
9、示。这里代码显示、图存储结构的显示和图的显示要同步起来。对于遍历时速度也需要进行设置。对于整个图的创建、遍历、销毁的代码实现,应该能够显示给用户看,而且还应该可以让用户感受一下该代码的运行效果。2.2 功能需求分析2.2.1 结点的操作结点用圆和圆上的字符来表示,圆上的字符表示结点的名称。当选择创建结点,在绘图区域点击鼠标的时候,在绘图区域绘制一个结点,并且在图的数据结构中添加一个结点,还要保存结点的位置。结点在创建的时候应该保证其名称的唯一性。当选择删除结点,在绘图区点击一个结点的时候,首先要检测鼠标点击位置是否有结点,如果有,则把保存该结点位置的信息以及和该结点相关的弧的位置信息删掉,并且
10、把该结点从图的数据结构中删除,然后重绘一下图。如果没有,则不做任何操作。2.2.2 弧的操作根据图类型的不同,弧的表示也不同。如果是有向图,则用一个箭头表示,箭头指向的是弧头,箭头的起始端是弧尾。如果是无向图,则用一条直线表示。当选择创建弧,在画图区域点击时,画一条直线或者一个箭头,并且在图的数据结构中添加一条弧,然后将弧的位置保存一下。弧的创建应该避免重复弧的产生。当选择删除弧,并且在画图区域点击时,先判断鼠标点击的位置是否存在一条弧,如果存在,则删除图数据结构中的选中的弧,并且删除保存的相应弧的位置信息,然后重绘一下图。2.2.3 自动生成图的支持有时为了图方便快捷,希望一键创建图,这时应
11、该可以点击一个按钮生成整个3图,图的生成就是结点和弧的创建过程,其实现原理和注意事项参照上面的结点的操作和弧的操作中创建结点和创建弧的部分。2.2.4 支持图的销毁当图的创建不是很满意的时候,可以点击清空按钮将图的结点位置信息和弧的位置信息清空,并且将图的数据结构销毁,然后清空绘图区域。2.2.5 图的遍历类型图的遍历分为深度优先遍历和广度优先遍历,程序对这两种方式都应该支持。选择不同的遍历方式,加载不同的初始化信息。2.2.6 图的存储结构图的遍历动态演示就是要动态地展示图是如何遍历其存储结构的。图遍历到哪里了,下一步应该访问哪个结点等都应该能准确地显示给用户。2.2.7 图的遍历代码图在遍
12、历时,应该根据代码一步一步地遍历,动态地展示代码运行到哪里了同样可以比较直观地体现图的遍历的整个流程。图的动态显示和图存储结构的动态显示应该根据图的代码执行步骤来动态显示。2.2.8 支持图的遍历次序显示和中间辅助队列的进出队情况显示图在遍历过程中,应显示中间辅助队列的进队和出队的情况。图遍历完成后还要显示一下遍历结果,也就是这种遍历方式的遍历次序的显示。2.2.9 支持对遍历速度的设置图的遍历速度不能固定,用户可以根据个人喜好来设置图的遍历速度,以便更好地体验图的遍历的整个过程。42.2.10 支持暂停和单步当程序在执行遍历的时候,如果想仔细查看当前的各项情况,可以暂停遍历,参看完成之后可以
13、点击运行继续执行,也可以点击单步查看每一步的执行情况。2.2.11 支持对图的实现代码的查看和运行只看遍历的那部分代码有时候还不够,需要对图的创建等都有相应的了解,这时可以查看图的整个实现代码,并且运行该代码。2.2.12 支持对版本和帮助的显示如果想查看版本和帮助信息,可以点击相应的菜单项查看。3 总体设计3.1 程序框架的搭建3.1.1 工程项目的创建利用 MFC 的应用程序向导创建一个名称为 GraphShow 的对话框工程项目。由于涉及到很多绘图的内容,为了避免多次界面重绘带来的闪屏或者界面卡死等可能性问题,这里保持对话框不能调整大小的默认属性。3.1.2 窗口的显示如图 1 所示,此
14、窗口的主体是向导帮我们生成的。首先我们删掉默认生成的一个 Static Text 控件和两个 Button 控件,然后向界面中添加三个 Static Text 控件、一个 ListBox 控件、两个 Radio Button 控件和三个 Button 控件(各个控件的属性如表 1 所示) 。再者,我们添加一个 Menu 资源,设置好其中的各项,并把本窗体的Menu 属性设置为刚添加的 Menu 资源的 ID。这样,我们就把窗体分为了这样几个区域:图的存储结构显示区域、图的显示区域、代码显示区域、遍历结果显示区域、菜单区域和其它按钮区域。图的存储结构显示区域由一个继承自 CStatic 类的5C
15、DrawLink 类来控制,这个类拥有一个 ALGraph 结构体类型的成员;图的显示区域由 CGraphDraw 类来控制,这个类也是继承自 CStatic;代码显示区域一个 CListBox类型的变量关联;遍历结果显示区域由继承自 CStatic 类的 CShowResult 类来控制;整个界面由 CGraphShowDlg 类来管理,它继承自 CDialog 类,并且拥有一个ALGraph 类型的成员。当按下执行按钮的时候,开启一个线程,这个线程遍历图的同时设置 List Box 中选中的行、画图中被访问的结点和图存储结构中被访问的结点。基本做到代码选中行的意思和图、存储结构的访问情况相
16、符合。图 1 图的遍历表格 1 图的遍历窗口上各控件属性控件类型 控件 ID 控件 Caption 其它Static Text IDC_STATIC_LINKStatic Text IDC_STATIC_GRAPHList Box IDC_LISTStatic Text IDC_STATIC_ORDERRadio Button IDC_RADIO_DFS 深度优先遍历 Group 属性为True6Radio Button IDC_RADIO_BFS 广度优先遍历Button IDC_BUTTON_RUN 执行Button IDC_BUTTON_STEP 单步执行Button IDC_BUTTO
17、N_PAUSE 暂停3.2 菜单的制作向工程中添加菜单资源,其资源 ID 为 IDR_MENU。然后添加“操作”和“关于”菜单项,分别有:创建图、设置演示速度、查看源代码、退出、版本和帮助这几个子菜单项。将此菜单加入到主界面中。如图 2 所示。图 2 主界面菜单向工程添加菜单资源,其 ID 为 IDR_MENU_CODE。然后添加“操作”菜单项,其内容分别是:运行此程序、打开此文件和退出。将此菜单加入到查看源代码界面中。如图 3 所示。图 3 查看源代码界面菜单3.2.1 创建图图 4 为创建图的界面。要绘制这个对话框,先向工程中添加一个 Dialog 资源,Dialog 资源的 ID 为 I
18、DD_DIALOG_CREATE,Caption 为“创建图” 。然后向 Dialog 中添加两个 Group Box 控件、六个 Radio Button 控件、一个 Static Text 控件和三个Button 控件,它们的属性设置如表 2 所示。这个窗口所有功能的代码实现在 CGraphCreate、 7CGraphDraw、ALGraph、VNode、ArcNode 这几个类或结构体中。其中 ArcNode 是表示弧的结构体,VNode 是表示结点的结构体,ALGraph 是表示邻接表法存储图的结构体。CGraphDraw 是一个继承自 CStatic 的类,它控制画图区域,它拥有一
19、个ALGraph 结构体类型的成员。CGraphCreate 是控制整个界面的类,它拥有CGraphDraw 类型的成员。界面中的两组单选按钮分别 CGraphCreate 类的两个成员变量关联,分别是 m_nType 和 m_nCreate,通过向 CGraphDraw 类传递这两个值来控制图的类型和许可的操作。图 4 创建图表格 2 创建图对话框上各控件属性控件类型 控件 ID 控件 Caption 其它Group Box 图的类型Radio Button IDC_RADIO_DG 有向图 Group 属性为TrueRadio Button IDC_RADIO_AG 无向图Static T
20、ext IDC_STATIC_DRAW8Group BoxRadio Button IDC_RADIO_CREATENODE 创建结点 Group 属性为TrueRadio Button IDC_RADIO_ CREATEARC 创建弧Radio Button IDC_RADIO_ DELETENODE 删除结点Radio Button IDC_RADIO_DELETEARC 删除弧Button IDC_BUTTON_AUTO 自动生成图Button IDC_BUTTON_CLEAN 清空Button IDC_BUTTON_AUTOCREATE 确定3.2.2 设置演示速度首先向工程中添加对话
21、框资源,设置其 ID 为 IDD_DIALOG_SETSPEED,Caption为“设置演示速度” 。然后向这个对话框中加入一个 Group Box 控件、两个 Static Text 控件、一个 Slider 控件和一个 Button 控件。最后设置 Group Box 的 Caption为“设置速度” ,两个 Static Text 的 Caption 分别为“慢”和“快” ,Slider 的 ID为 IDC_SLIDER1,Button 的 Caption 为“确定” 、ID 为 IDC_BUTTON_SETSPEED。结果如图 4。把这个对话框和 CSetSpeed 类关联起来,把 S
22、lider 控件和 CSetSpeed 类的一个整型成员变量 m_nCur 和一个 CSliderCtrl 类型的成员变量 m_ctrlSpeed 关联起来。在初始化对话框的时候,通过调用 m_ctrlSpeed.SetRange(int,int)来设置滚动条的范围,调用 m_ctrl.SetPos(int)来设置滚动条的位置。当用鼠标拖动滚动条的时候会产生 WM_HSCROLL 消息。在这个消息的处理里面获得滚动条的位置,给m_nCur 赋值,这个值穿给主对话框对应的类后,经过一定的计算就可以控制演示的速度了。9图 5 演示速度设置3.2.3 查看源代码的实现首先向工程中添加一个对话框资源,
23、其 ID 为 IDD_DIALOG_RESOURCE,Caption为“源程序” 。然后在对话框中添加一个 List Box 控件,设置其 ID 为IDC_LIST_RESOURCE,Horizontal Scroll 设置为 True。并且把 ID 为IDR_MENU_CODE 的菜单添加到对话框的中。把 List Box 和一个 CListBox 类型的对象 m_cltbResoure 关联起来。在对话框初始化的时候读取 code.c 这个文件,将其中的内容加入到 List Box 控件中。图 6 源代码查看103.2.4 运行此程序菜单的实现点击此菜单,调用 CreateProcess
24、函数创建一个子进程,这个子进程打开一个控制台窗口,该控制台可以实现对结点和弧的添加、插入和删除操作,以及图的深度优先遍历和广度优先遍历。如图 7 所示。图 7 源代码执行3.2.5 打开此文件菜单和帮助菜单的实现点击菜单的时候,调用 ShellExecuteEx 让电脑上的软件打开相应的文件。3.2.5 版本菜单的实现由于使用应用程序向导创建 MFC Dialog 类型的应用程序的时候,默认会插入一个 ID 为 IDD_ABOUTBOX 的对话框。该对话框关联的类为 CAboutDlg,我们在该对话框内添加几个 Static Text 来实现对版本信息的显示。3.2.6 退出菜单功能的实现当点
25、击退出菜单的时候,调用 EndDialog 关闭对话框,进而关闭整个程序。113.3 图的创建和遍历核心算法的设计与实现3.3.1 算法的设计图的存储,可以用邻接表法表示图。邻接表是图的一种链式存储结构。在邻接表中,对图中每个顶点简历一个单链表,第 i 个单链表中的结点表示依附于顶点 vi的边(对有向图是以顶点 vi为尾的弧)。每个结点由 3 个域组成,其中邻接点域指示与顶点 vi邻接的点在图中的位置,链域指示下一条边或弧的结点;数据域存储和边或弧相关的信息,如权值等。每个链表上附设一个表头结点。在表头结点中,除了设有链域指向链表中第一个结点之外,还设有存储顶点 vi的名或其他有关信息的数据域
26、。如下图所示表结点 头结点这些表头结点(可以链相接)通常以顺序结构的形式存储,以便随机访问任一顶点的链表。图的遍历是从图中某一顶点触发访遍图中其余顶点,且使每一个顶点仅被访问一次。通常有两条遍历图的路径:深度优先遍历和广度优先遍历。图的深度优先遍历类似于书的先根遍历,是树的先根遍历的推广。假设初始化状态是图中所有顶点未曾被访问,则深度优先遍历可从图中某个顶点 v 出发,访问此顶点,然后依次从 v 的未被访问的邻接点出发深度优先遍历图,直至图中所有和v 有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。图
27、的广度优先遍历类似于树的按层次遍历的过程。假设从图中顶点 v 出发,在访问了 v 之后依次访问 v 的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先遍历的过程是以 v 为起始点,由近至远,依次访问和 v 有路径相通且路径长度为 1,2,的顶点。由于本次毕业设计需要动态的演示图的遍历过程,这涉及到绘图的问题。因此,adjvex n
28、extarc info data firstarc12在图的结点和弧创建的时候都需要记录他们的位置,并且设置他们的访问标志为未被访问。当图中的结点和弧被删除的时候,需要删除对应的结点和弧,并且把保存的对应的位置和访问信息重置。当图中的结点和弧被访问的时候要设置他们的访问标志为已经被访问。在创建节点的时候,其算法流程如图 8 所示。图 8 创建结点当选择删除结点时,其具体的处理流程路图 9 所示。是鼠标点击结束是否选择了删除结点按钮鼠标点击是否命中结点删除保存的结点信息、删除和该结点相连接的弧的位置信息、删除命中结点,更新保存的弧的位置信息、擦除界面上结点和相关弧否否是是否是否结点数是否已达到最
29、大限度鼠标点击创建结点按钮是否被选中创建结点保存该结点并设置其未被访问在鼠标点击位置画结点结束13图 9 结点的删除在判断鼠标点击是否命中结点的时候,需要遍历所有结点,求出每一个结点的圆心到鼠标点击位置的长度,其具体的计算公式为:长度 L = sqrt(pow(y2 y1, 2) + pow(x2 x1, 2))如果长度小于结点圆的半径,则为命中了该节点。在创建弧的时候,具体的操作流程如图 10 所示。结束设置节点未被选中调换弧头和弧尾的位置创建弧,保存弧的位置,并且设置其为未访问然后画弧创建弧,保存弧的位置,并且设置其为未访问然后画弧创建弧,保存弧的位置,并且设置其为未访问然后画弧无向图 有
30、向图否是即将要创建的弧是否已经存在设置该结点为选中否是是否有结点上次选定了是否否是鼠标点击创建弧按钮是否被选中鼠标点击是否命中了结点14图 10 创建弧创建弧时,为了画得好看,并不是从两个节点的圆心画一条线,而是从两圆心的连线与圆周的交点处画线,这两个坐标的具体求解方法如图 11.图 11 求两结点圆心连线与圆周交点辅助图假设两个圆的圆心坐标为 A(x1,y1) 、B(x2,y2) ,则 AB 直线的长度为:|AB|=sqrt(pow(x2-x1,2)+pow(y2-y1,2)利用相似三角形求解,可知(y2-y1)/y=|AB|/R,求解得出 y=abs(y2-y1)*R/sqrt(pow(x
31、2-x1,2)+pow(y2-y1,2),同理可以得出 x=abs(x2-x1)*R/sqrt(pow(x2-x1,2)+pow(y2-y1,2)。则与两圆周交点的坐标为:(x2x1?x1+x:x1-x,y2y1?y1+y:y1-y)(x1x2?x2+x:x2-x,y1y2?y2+y:y2-y)15然后以这两个交点为起始点画直线或者画箭头即可。至于画箭头的数学推导,这里就不详细说明,只是在算法实现里面给出实现代码。删除弧的处理流程如图 11 所示。图 11 删除弧其中,删除弧的时候要判断鼠标点击时是否命中弧,其具体的算法为:遍历每一条弧,根据弧头和弧尾的坐标,进行如下三种情况下的判定:当弧的两
32、个结点位置的 x 坐标相差不到 2 个像素的时候,可以规划一个矩形区域,鼠标点击在这个矩形区域即为命中。这个矩形区域的 left 值为两个结点中任意一个结点的 x 值减 3,right 值为该任意结点的 x 值加 3,top 值为结点中较小的那个 y值,bottom 为结点中较大的那个 y 值。当弧的两个结点位置的 y 坐标相差不到 2 个像素的时候,可以规划一个矩形区域,鼠标点击在这个矩形区域即为命中。这个矩形区域的 top 值为两个结点中任意一个结点的 y 值减 3,bottom 值为该任意结点的 y 值加 3,left 值为结点中较小的那个x 值,right 为结点中较大的那个 x 值。
33、排除上面两种情况后,应该这么判定弧是否被选中:首先根据弧的弧头和弧尾的坐是否是否鼠标点击是否选择了删除弧单选按钮鼠标点击时是否命中弧删除该弧的位置信息、删除弧、擦除弧结束16标写出这两点之前的直线方程 y = (y2 y1)*(x x1)/(x2 x1)。把鼠标点击时位置的 x 坐标值代入到该方程中,如果得到的 y 值和鼠标点击时位置的 y 值差距在 3 个像素以内,并且鼠标点击的位置在弧头和弧尾组成的矩形区域内,则为命中弧。3.3.2 核心算法的实现图的遍历动态演示程序主要分为图的创建和图的遍历。用邻接表法实现图,数据结构为:#define MAX_VERTEX_NUM 26typedef
34、enumDG,AG,DN,AN GraphKind;typedef struct ArcNodeint adjvex;/该弧所指向的顶点的位置struct ArcNode *nextarc;/指向下一条弧的指针InfoType *info;/该弧相关信息的指针ArcNode;typedef struct VNodeVertexType data;/顶点信息ArcNode *firstarc;/指向第一条依附该顶点的弧的指针VNode;typedef struct ALGraphvector vertices;int vexnum,arcnum;/ 图的当前顶点数和弧数int kind;/图的种
35、类标志ALGraph;创建结点的具体代码实现为:void ALGraph:CreateNode()VNode node;17if (vertices.size() = 0)/图中没有结点,则将结点名字标记为A。node.data = “A“;else/图中有结点则将名字标记为上个结点的名字的ascii码加1得到的字符node.data = vertices.back().data.at(0); node.data.at(0)+;vertices.push_back(node);/将结点加入到图中创建弧的具体代码实现为:void ALGraph:CreateArc(int BegPos, int
36、 EndPos)ArcNode *arc = NULL;arc = new ArcNode();/新建一条弧arc-adjvex = EndPos;/设置该弧指向EndPo位置的结点arc-info = NULL;/不为其设置权值。/将本弧插入到位序为 BegPos 结点链表的前面arc-nextarc = vertices.at(BegPos).firstarc; vertices.at(BegPos).firstarc = arc;删除结点的具体代码实现为:void ALGraph:DeleteNode(VertexType v)int i, j;ArcNode *p = NULL, *q
37、 = NULL;j = LocateVex(v);/获取v结点的位序if (j nextarc;if (kind / 2)free(q-info);free(q);arcnum-;vexnum-;/结点数减少1for (i = j; i adjvex = j)if (p = vertices.at(i).firstarc)vertices.at(i).firstarc = p-nextarc;if (kind / 2)free(p-info);free(p);arcnum-;p = vertices.at(i).firstarc;elseq-nextarc = p-nextarc;if (ki
38、nd / 2)free(p-info);19free(p);arcnum-;p = q-nextarc;elseif (p-adjvex j)p-adjvex-;q = p;p = p-nextarc;删除弧的具体代码实现为:Status ALGraph:DeleteArc(VertexType v, VertexType w)ArcNode *p = NULL, *q = NULL;int i, j;i = LocateVex(v); /* i是顶点v(弧尾)的序号*/j = LocateVex(w); /* j是顶点w(弧头)的序号*/if(i adjvex != j) /* p不空且所指
39、之弧不是待删除弧 */ /* p指向下一条弧*/q = p;p = p-nextarc;if(p /* 指向下一条弧 */20elseq-nextarc = p-nextarc; /* 指向下一条弧*/if(kind / 2) /* 网*/free(p-info);arcnum-; /* 弧或边数减 */return OK;图广度优先遍历动态演示的代码实现:void CGraphShowDlg:BFSTraverse(void)int NodeRow, NodeCol, ArrowRow, ArrowCol;visited.clear();/清空记录被访问情况的队列m_listbox.SetC
40、urSel(4);Sleep(m_nTime);m_listbox.SetCurSel(5);Sleep(m_nTime);/设置每个结点都没有被访问for (int i = 0; i != g.vexnum; +i)visited.push_back(FALSE);m_listbox.SetCurSel(6);Sleep(m_nTime);m_listbox.SetCurSel(5);Sleep(m_nTime);m_listbox.SetCurSel(7);Sleep(m_nTime);deque Q;m_listbox.SetCurSel(8);Sleep(m_nTime);ArcNod
41、e *arc = NULL;/遍历每一个结点组成的链表for (int v = 0; v adjvex)m_listbox.SetCurSel(21);Sleep(m_nTime);visited.at(arc-adjvex) = TRUE;m_ctrlShowResult.PushNode(g.vertices.at(arc-adjvex).data);m_cDraw.ScanNode(arc-adjvex);m_cLink.ScanNode(NodeRow, NodeCol+);m_cLink.ScanArrow(ArrowRow, ArrowCol+);m_cDraw.ScanArc(N
42、odeRow, arc-adjvex);m_listbox.SetCurSel(22);Sleep(m_nTime);m_listbox.SetCurSel(23);Sleep(m_nTime);Q.push_back(arc-adjvex);m_ctrlShowResult.PushData(g.vertices.at(arc-adjvex).data);elsem_cLink.ScanNode(NodeRow, NodeCol+);m_cLink.ScanArrow(ArrowRow, ArrowCol+);m_cDraw.ScanArc(NodeRow, arc-adjvex);m_li
43、stbox.SetCurSel(18);Sleep(m_nTime);arc = arc-nextarc;m_listbox.SetCurSel(8);Sleep(m_nTime);m_listbox.SetCurSel(27);Sleep(m_nTime);/设置按钮是否可以操作23(CButton*)GetDlgItem(IDC_BUTTON_RUN)-EnableWindow(TRUE);(CButton*)GetDlgItem(IDC_BUTTON_STEP)-EnableWindow(TRUE);(CButton*)GetDlgItem(IDC_BUTTON_PAUSE)-Enabl
44、eWindow(FALSE);图的深度优先遍历动态演示的代码实现如下:void CGraphShowDlg:DFSTraverse(void)visited.clear();/清空标记结点是否被访问的队列m_listbox.SetCurSel(17);Sleep(m_nTime);/设置所有结点都没有被访问for (int i = 0; i != g.vexnum; +i)visited.push_back(FALSE);m_listbox.SetCurSel(18);Sleep(m_nTime);m_listbox.SetCurSel(17);Sleep(m_nTime);m_listbox
45、.SetCurSel(19);Sleep(m_nTime);/深度优先遍历整个图for (int v = 0; v EnableWindow(TRUE);(CButton*)GetDlgItem(IDC_BUTTON_STEP)-EnableWindow(TRUE);(CButton*)GetDlgItem(IDC_BUTTON_PAUSE)-EnableWindow(FALSE);24void CGraphShowDlg:DFS(int v)int NodeRow(0), NodeCol(0), ArrowRow(0), ArrowCol(0);NodeRow = v;ArrowRow =
46、v;m_listbox.SetCurSel(6);Sleep(m_nTime);m_listbox.SetCurSel(7);Sleep(m_nTime);visited.at(v) = TRUE;m_ctrlShowResult.PushNode(g.vertices.at(v).data);/输出遍历的节点m_cDraw.ScanNode(v);/在图中画被访问的节点m_cLink.ScanNode(NodeRow, NodeCol+);/在图存储结构图中画被访问的结点m_listbox.SetCurSel(8);Sleep(m_nTime);ArcNode *arc = g.vertic
47、es.at(NodeRow).firstarc;m_listbox.SetCurSel(9);Sleep(m_nTime);while (arc != NULL)/遍历该结点的出度链表m_listbox.SetCurSel(10);Sleep(m_nTime);m_cLink.ScanNode(NodeRow, NodeCol+);m_cLink.ScanArrow(ArrowRow, ArrowCol+);m_cDraw.ScanArc(NodeRow, arc-adjvex);if (!visited.at(arc-adjvex)/如果arc-位置的结点没有被访问则递归m_listbox.
48、SetCurSel(11);Sleep(m_nTime);DFS(arc-adjvex);m_listbox.SetCurSel(9);Sleep(m_nTime);arc = arc-nextarc;/下一条弧m_listbox.SetCurSel(12);Sleep(m_nTime);实现画弧的源代码:void CGraphDraw:DrawArrow(Node p1, Node p2)/画箭头double theta = 15;/箭头主线和两边的偏转角度int length = 10;/箭头两边的长度25/计算两结点圆心与圆交点的位置坐标int x = abs(p2.Point.x-p1
49、.Point.x)*10/sqrt(pow(static_cast(p2.Point.x-p1.Point.x),2)+pow(static_cast(p2.Point.y-p1.Point.y),2);int y = abs(p2.Point.y-p1.Point.y)*10/sqrt(pow(static_cast(p2.Point.x-p1.Point.x),2)+pow(static_cast(p2.Point.y-p1.Point.y),2);p1.Point.x = p2.Point.xp1.Point.x?p1.Point.x+x:p1.Point.x-x;p1.Point.y = p2.Point.yp1.Point.y?p1.Point.y+y:p1.Point.y-y;p2.Point.x = p1.Point.xp2.Point.x?p2.Point.x+x:p2.Point.x-x;p2.Point.y = p1.Point.yp2.Point.y?p2.P