数据结构课程设计.docx

上传人:b****2 文档编号:18250266 上传时间:2023-04-24 格式:DOCX 页数:21 大小:405.56KB
下载 相关 举报
数据结构课程设计.docx_第1页
第1页 / 共21页
数据结构课程设计.docx_第2页
第2页 / 共21页
数据结构课程设计.docx_第3页
第3页 / 共21页
数据结构课程设计.docx_第4页
第4页 / 共21页
数据结构课程设计.docx_第5页
第5页 / 共21页
点击查看更多>>
下载资源
资源描述

数据结构课程设计.docx

《数据结构课程设计.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计.docx(21页珍藏版)》请在冰豆网上搜索。

数据结构课程设计.docx

数据结构课程设计

算法与数据结构

课程设计

 

班级:

计科10801

姓名:

指导老师

 

2010年6月14-2010年6月25日

课程设计题目:

图的基本操作及应用

一.引言

数据结构是一门理论性强、思维抽象、难度较大的课程,是基础课和专业课之间的桥梁。

该课程的先行课程是计算机基础、程序设计语言、离散数学等,后续课程有操作系统、编译原理、数据库原理、软件工程等。

通过本门课程的学习,我们应该能透彻地理解各种数据对象的特点,学会数据的组织方法和实现方法,并进一步培养良好的程序设计能力和解决实际问题的能力,而且该课程的研究方法对我们学生在校和离校后的学习和工作,也有着重要的意义。

数据结构是计算机科学与技术专业的一门核心专业基础课程,在该专业的课程体系中起着承上启下的作用,学好数据结构对于提高理论认知水平和实践能力有着极为重要的作用。

学习数据结构的最终目的是为了获得求解问题的能力。

对于现实世界中的问题,应该能从中抽象出一个适当的数学模型,该数学模型在计算机内部用相应的数据结构来表示,然后设计一个解此数学模型的算法,再进行编程调试,最后获得问题的解答。

二.设计目的

1.能根据实际问题的具体情况,结合数据结构课程中的基本理论和基本算法,分析并正确确定数据的逻辑结构,合理地选择相应的存储结构,并能设计出解决问题的有效算法。

2.提高程序设计和调试能力。

学生通过上机实习,验证自己设计的算法的正确性。

学会有效利用基本调试方法,迅速找出程序代码中的错误并且修改。

3.初步掌握软件开发过程中问题分析、系统设计、程序编码、测试等基本方法和技能。

4.训练用系统的观点和软件开发一般规范进行软件开发,培养软件工作者所应具备的科学的工作方法和作风。

5.培养根据选题需要选择学习书籍,查阅文献资料的自学能力。

三.设计方案

本次课设是为了实现图的基本操作及运用,由于设计操作比较多,我们可以用代码的模块化来实现,这样看起来比较直观,也比较有条理,根据图的类别可以分为四大块:

无向图,无向网,有向图以及有向网。

然后在加上一个总体的菜单模块,分三级菜单实现。

其实现的内容如下:

1.无向图的基本操作及应用

1创建无向图的邻接矩阵

2创建无向图的邻接表

3无向图的深度优先遍历

4无向图的广度优先遍历

2.无向网的基本操作及应用

1创建无向网的邻接矩阵

2创建无向网的邻接表

3求最小生成树

3.有向图的基本操作及应用

1创建有向图的邻接矩阵

2创建有向图的邻接表

3拓扑排序

4.有向网的基本操作及应用

1创建有向网的邻接矩阵

2创建有向网的邻接表

3关键路径

4单源最短路径

5每对顶点之间的最短路径

菜单流程图如下图:

 

四.实现过程

1.图的存储结构

本次课设中主要采用邻接表和邻接矩阵的存储方式。

邻接表:

邻接表是图的一种链式存储结构。

在邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点Vi的边(对有向图是以顶点Vi为尾的弧)。

每个结点由3个域组成,其中邻接点域(adjvex)指示与顶点Vi邻接的点在图中的位置,链域(nextarc)指示下一条边或弧的结点;数据域(info)存储和边或弧相关的信息,如权值等。

每个链表上依附一个表头结点。

在表头结点中,除了设有链域(firstarc)指向链表中第一个结点之外,还设有存储顶点Vi的名或其他有关信息的数据域(data)。

邻接矩阵:

设G=(V,E)是具有n个顶点的图,则G的邻接矩阵是具有如下性质的n阶方阵:

      

 

 从图的邻接矩阵表示法中可以得到如下结论:

  

(1)对于n个顶点的无向图,有A(i,i)=0,1≤i≤n。

  

(2)无向图的邻接矩阵是对称的,即A(i,j)=A(j,i),1≤i≤n,1≤j≤n。

  (3)有向图的邻接矩阵不一定对称的。

因此用邻接矩阵来表示一个具有n个顶点的有向图时需要n2个单位来存储邻接矩阵;对有n个顶点的无向图则需存入上(下)三角形,故只需n(n+1)/2个单位。

  (4)无向图的邻接矩阵的第i行(或第i列)非零元素的个数正好是第i个顶点的度TD(vi)。

  (5)有向图的邻接矩阵的第i行(或第i列)非零元素的个数正好是第i个顶点的出度OD(vi)[或入度ID(vi)]。

2.图的遍历

图的遍历是有关于图的算法中最常见、最典型的算法。

与树的遍历相类似,图的遍历是从图中任意一个顶点出发,访问遍图中所有的顶点,且使每个顶点仅被访问一次。

图的遍历算法是求解图的连通性、拓扑排序、和求关键路径等算法的基础。

由于图本身结构的复杂性,因而使得图的遍历要比树的遍历复杂得多。

首先,图中所有顶点没有主次之分,因此也就没有一个“自然”的起始点;其次,图中任意顶点均有可能与其它顶点相邻,在沿着某一路径依次搜索访问顶点时完全有可能又回到该顶点上;此外,图中某一顶点可能与多个顶点相邻,当访问过该结点后,如何选择下一个要访问的顶点,就成为一个决策问题。

鉴于图的遍历的复杂性,遍历算法的设计就必须考虑图的结构特征。

图的遍历算法通常有两条遍历路径:

深度优先遍历和广度优先遍历。

这两种遍历既适用于无向图,也适用于有向图。

深度优先遍历方法的思想是:

假设初始状态时图中所有顶点均未被访问过,从图中任意指定的顶点V出发,先访问此顶点,然后基于邻接表结构访问依附于该顶点的第一个未被访问过的邻接顶点,接着仍基于邻接表结构从这个新被访问过的顶点出发,访问依附于该顶点的第一个未被访问过的邻接顶点……如此访问下去直至到达一个所有邻接顶点都被访问过的顶点为止;然后倒过来依次回退到还有未被访问过的顶点,重复上述过程,直到图中所有与V有路径相通的顶点都被访问过。

如果此时图中尚有没有被访问过的顶点,则再从其中一个未被访问过的顶点出发重复上述过程,直至图中所有顶点均被访问过为止。

广度优先遍历的思想是:

假设初始状态图中各顶点均未被访问过,从图中任意顶点V出发,在访问该顶点后,基于邻接表结构依次访问该顶点的所有未被访问过的邻接顶点,然后分别从这些顶点出发,再依次访问它们的未被访问过的邻接顶点,并使得先被访问的顶点的邻接顶点先于后被访问的顶点的邻接顶点被访问;即从V开始访问,若它的邻接顶点依次是W,X,Y……,则依次访问W,X,Y……,然后再访问W顶点的邻接顶点,再访问X顶点的邻接顶点,再访问Y顶点的邻接顶点……,如此下去直至图中所有被访问过的顶点的邻接顶点均被访问过为止。

若此时尚有未被访问过的顶点,则再从此顶点出发,重复上述过程,直到全部顶点都被访问一遍后结束。

结论:

广度优先遍历相当于对广度优先生成树进行层序遍历。

特别提示:

对图这样的逻辑结构进行遍历,其算法通常是基于邻接表结构展开的。

但无论采用深度优先遍历还是广度优先遍历方法,初始选定的顶点不同,其遍历序列也不会相同,因此,遍历结果取决于三个要素:

邻接表的形态(即图的存储结构);算法策略;选定的起始顶点。

当三者其一发生改变,则遍历结果将随之发生变化。

3.最小生成树

Prim算法是首先从v中任取一个顶点u0,将生成树T置为仅有一个结点u0的树,即置U={u0};然后只要U是V的真子集,就在所有那些其一个端点u己在T(即u∈U)、另一个端点v还未在T(即v∈V—U)的边中,找一条最短(即权最小)的边(u,v),并把该条边(u,v)和其不在T中的顶点v,分别并入T的边集TE和顶点集U。

如此进行下去,每次往生成树里并入一个顶点和一条边,直到把所有顶点都包括进生成树T为止。

此时,必有U=V,TE中有n-1条边。

MST性质保证上述过程求得的T=(U,TE)是G的一棵最小生成树。

Kruskal算法是一开始,先将G图中的边都去掉,只留下孤立的顶点,这个图即为G图最初的生成子图G1。

然后逐步地将当前最小边e1加上去,每次加的时候,要保持住“没有圈”这一性质,在加了N—l条边(N是顶点个数)后,G1便成为所要求的最小生成树了。

4.拓扑排序

简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序,直观地看,偏序指集合中仅有部分成员之间可比较,而全序指集合中全体成员之间均可比较。

实现拓扑排序的方法很简单:

(1)在有向图中选一个没有前驱的顶点且输入之。

(2)从图中删除该顶点和所有以它为尾的弧。

重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。

5.关键路径

在一个带权的有向无环图中,顶点表示事件,弧表示活动,权表示活动持续的时间。

路径长度是指路径上各活动持续时间之和,不是路径上弧的数目,路径长度最长的路径叫做关键路径。

假设开始点是V1,从V1到Vi的最长路径长度叫做时间Vi的最早发生时间,这个时间决定了所有以Vi为尾的弧锁表示的活动的最早开始时间,用e(i)表示活动ai的最早开始时间,还可以定义一个活动的最迟开始时间l(i),两者之差l(i)-e(i)意味着完成活动ai的时间余量。

辨别关键活动就是要找e(i)=l(i)的活动。

6.最短路径

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,算法的基本思想:

假设每个点都有一对标号(dj,pj),其中dj是从起源点s到点j的最短路径的长度(从顶点到其本身的最短路径是零路(没有弧的路),其长度等于零);pj则是从s到j的最短路径中j点的前一点。

Floyd(弗洛伊德)算法基本思想:

从代表任意2个顶点Vi到Vj的距离的带权邻接矩阵开始,用cij表示从Vi到Vj的距离(费用、时间),每次插入一个顶点Vk,然后将vi到vj间的已知最短路径与插入顶点Vk作为中间顶点(一条路径中除始点和终点外的其他顶点)时可能产生的Vi到Vj路径距离比较,取较小值以得到新的距离矩阵。

如此循环迭代下去,依次构造出n个矩阵(或表格)L1,L2,...Ln,当所有的顶点均作为任意2个顶点Vi到Vj中间顶点时得到的最后的带权邻接矩阵Ln反映了所有顶点对之间的最短距离信息,成为图G的距离矩阵。

最短路径求法:

定义d[i]为在已产生的最短路径中加入一条最短边的长度,从而使得扩充的路径到达顶点i。

最初,仅有从k到k的一条长度为0的路径,这时对于每个顶点i,d[i]等于a[k][i](a是有向图的长度邻接矩阵)。

为产生下一条路径,需要选择还未产生最短路径的下一个节点,在这些节点中d值最小的即为下一条路径的终点。

当获得一条新的最短路径后,由于新的最短路径可能会产生更小的d值,因此有些顶点的d值可能会发生变化,更新d[j]值为min{d[j],d[i]+a[i][j]}。

所有顶点对之间的最短路径是指:

对于给定的有向网G=(V,E),要对G中任意一对顶点有序对V、W(V≠W),找出V到W的最短距离和W到V的最短距离。

解决此问题的一个有效方法是:

轮流以每一个顶点为源点,重复执行迪杰斯特拉算法n次,即可求得每一对顶点之间的最短路径,总的时间复杂度为O(n3)。

五.测试结果

1无向图

邻接矩阵的创建:

邻接表的创建:

深度优先遍历:

广度优先遍历:

 

2无向网

邻接矩阵的创建:

邻接表的创建:

 

Prim算法:

Kruskal算法:

 

3有向图

邻接矩阵的创建:

邻接表的创建:

 

拓扑排序:

4有向网

邻接矩阵的创建:

邻接表的创建:

关键路径:

单源顶点最短路径:

 

每对顶点最短路径:

六.使用说明

本次课设的编译平台是VC6.0,下面我将以关键路径的求解来说明一下该如何使用本程序。

本程序是以模式菜单的模式来提示读者输入相关信息,主菜单模式如下图:

由于关键路径的求解是有向网中的一个具体操作,因此选择菜单4,即输入数字4,输入后将跳到如下图所示的界面:

按要求应该选择子菜单3,即输入数字3,将会出现提示文字,按提示文字输入相关信息,输入完成后按回车键即可打印出结果,其效果如下图:

其他的使用方法类比这一例子都可得出运行结果,在这里就不一一列举了。

七.可改进的地方

由于本次课设时间安排比较紧,所以在程序上有一些不足,比如说在与用户的交互上做得不是很好,提示语句较少,而且在输出格式上也做得不够完善,排版有些杂乱,也存在一定的资源浪费。

在遍历过程设计中,开始存在遍历的开始顶点不明,结果出现差异,经过添加与使用者的交互,以及对代码的改进,再经过多次调试,使得输出界面明朗化,给操作者的提示也有了增加。

当然还有一些程序也存在类似的问题,这里由于自身的知识功底不足,暂时没有改进的能力。

在代码的模块化上,虽然在总体上看起来还不错,不过在某些细节上还做得不够。

在时间复杂度和空间复杂度上也有待改进,使程序的内存占用能有降低。

八.难点和收获

难点:

本次课设主要是实现图的基本操作与应用,总体看来难度一般,主要是在细节的把握上,以及代码模块化上,要下一些功夫,还有就是有些语法比较难懂,需要仔细的研读。

图是数据结构中的一个重点,也是一个难点,其应用范围较广,所包含知识点多,因此在课设的时候参考了不少资料。

遍历的过程是从图的某一顶点开始,存在随意性,其复杂度比树要复杂得多,在开始编写时,没有注意到这一点,以致在测试时对输出结果的把握上出现偏差。

拓扑排序也是一个难点,它要考虑图中是否存在环路,只有不存在环的图中才能输出其拓扑序列。

在图的应用中有几个关键算法,例如prim(普里姆)算法,kruskal(克鲁斯卡尔)算法求最小生成树,关键路径的求解,dijkstra(迪杰斯特拉)算法,floyd(弗洛伊德)算法求最短路径,都是本次课设的难点,这是图中很重要的应用。

当然这些算法中各自也有相对的优越性,prim算法适用于求边稠密的网的最小生成树,kruskal算法相对于prim算法而言,它更适合于求边稀疏的网的最小生成树;dijkstra算法适用于求单源顶点各顶点的最短路径,floyd算法则更适合于求每一对顶点的最短路径。

因此在算法的选择上,合理性这是一个难点。

代码的模块化,是在设计一个项目的重中之重,它不仅使我们所编写的代码在界面上看起来更美观,在使用上也更简单,更容易上手。

对一个设计者而言,代码的模块化是我们必须重视的问题。

收获:

通过这一段时间的课设,我学到了数据结构课堂上学不到的知识,认识上也有了一定的提高,对数据结构这门课程有了进一步的了解。

数据结构重在算法,他是数据的一种组织形式。

做了这次课程设计,我觉得课程设计这种形式真的是我们需要的,可以让我们学到很多,包括书上的、书外的。

课设相当于一次对课堂上所学知识的一次综合大演练,对我们理解课本,理解程序有很大的帮助。

不仅如此,在程序的编写过程中了学会了如何查找相关资料,在查找资料的同时也深感自己知识的局限性,以及基本功的不牢靠。

基础的牢靠对我们来说相当重要,不论是在每学期的课设中,还是在我们以后的行业从事中也起着重要的作用。

正比如在建筑物的建设中,如果你的地基打得不够牢靠,那么你建的再高,也是一座危房。

每次课设都是一个考验人耐心的时候,只有真正的沉下心来,才能在完成任务的同时自己能收获课堂上学不到的知识,才能拓宽自己在专业方面的知识面,才能领悟编程的主要思想课设的目的不仅仅是让我们得出结果以及一大堆代码就完事了,而是让我们学习编程的思想,只有在编程的思想上有了提高,才能算上真正的提高。

培养一种编程的思想,对我们每个计算机专业的学生来说,是最根本的,也是最重要的。

还有一点,就是理论和实践两者永远是戚戚相关的。

在实践的上机操作中,不仅是让我们了解数据结构的理论知识,更重要的是培养解决实际问题的能力,所以相信通过此次课设可以提高我们分析设计能力和编程能力,为后续课程的学习及实践打下良好的基础。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1