1、 青岛理工大学 数据结构课程设计报告 题目: 关键路径的实现 院(系): 计算机工程学院 学生姓名: 班级: 学号: 起迄日期: 2014.7.8—2014.7.19 指导教师: 张艳 一、需求分析 1.问题描述 找出实际工程中的关键路径,合理安排关键活动的施工顺序。要求: (1)表示工程的图可以用邻接表或邻接矩阵存储; (2)应能以图形的方式输出图; (3)输出关键路径和关键活动。 2.基本功能 (
2、1)用邻接表存储有向图并建立AOE网 CreateGraph(); (2)用图形的形式输出有向图Display(); (3)输出关键路径和关键活动 SearchMapPath(); 3.输入输出 输入: (1)有向图的顶点数和弧数,都是int型,中间用空格隔开; (2)图中的各个顶点的值,char型; (3)图中弧的权值、起点、终点,都是int型,中间用空格隔开; 输出: 起点(char)、终点(char) 、最早开始时间(int)、最迟开始时间 (int)、差值(int)、是否为关键活动、关键路径。 二、 概要设计 1.设计
3、思路: (1) 输入图的顶点数和弧数。 (2) 输入这个图中每段弧的起始点及权值。 (3) 用输入的数据建立AOE网。 (4) 用邻接表来存储图的这些信息。 (5) 用CreateGraph( )函数建立AOE图。 (6)用Display()函数输出AOE图。 (7) 用SearchMapPath ( )函数求出最长路径,并输出关键路径。 (8) 编写程序。 2.数据结构设计: (1)逻辑结构采用图状的结构。图是一种较线性表和树更为复杂的数据结构。在线性表中,数据元素之间仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继;在树形结构中,数据元素之间
4、有着明显的层次关系,并且每一层上的数据元素可能和下一层中多个元素(即其孩子结点)相关,但只能和上一层中一个元素(即其双亲结点)相关;而在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。而由于本程序的操作对象是有向图,所以必须采用图状的结构。 (2)存储结构采用链式的结构。由于图的结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据元素在存储区中的物理位置来表示元素之间的关系,即图没有顺序映象的存储结构,因此采用链式的存储结构。 (3)抽象数据类型图的定义如下: ADT Graph{ 数据对象V:V是具有相同特性的数据元素的集合,称为顶点集。
5、
数据关系R:
R={VR}
VR={
6、}ADT Graph 3. 软件结构设计: 三、 详细设计 1. 定义程序中所有用到的数据及其数据结构: 邻接表的存储单元: typedef struct node { int adjvex; int w; struct node *nextedge; }edgenode; 表头结点: typedef struct { char data; int id; int x,y; //顶点的
7、横坐标、纵坐标 edgenode *firstedge; }vexnode; 2. 主函数和其他函数的伪码算法: 主函数: void main() { int vexnumber,arcnumber; printf("请输入这个图中的节点数和弧数:"); scanf("%d %d",&vexnumber,&arcnumber); vexnode* Graph=(vexnode*)malloc(vexnumber*sizeof(vexnode)); CreateGraph(Grap
8、h,vexnumber,arcnumber);
SearchMapPath(Graph,vexnumber,arcnumber);
}
建立AOE网函数:
void CreateGraph(vexnode* Graph,int vexnumber,int arcnumber)
{ int begin,end,duttem;
char ch;
edgenode *p;
//输入顶点信息存储在顶点表中,并初始化该顶点的便表。
for(int i=0;i 9、[i].id =0;
Graph[i].firstedge =NULL;
}
//输入边所依附的两个顶点的序号i和j然后生成新的邻接点序号为j的//边表结点,将该结点插入到第i个表头部。
printf("请输入这个图中的各个顶点的值:\n");
for(i=0;i 10、
for(int k=0;k 11、gin-1].firstedge =p;
}
}
显示有向图函数:
void Display(vexnode* Graph,int vexnumber,int arcnumber)
{
int i;
int arw[6];
edgenode *p;
initgraph(400, 600);
for(i=0;i 12、i].y=50*(i+1);
}
if(i%3==1||i==1)
{
outtextxy(10,50*(i+1), Graph[i].data);
Graph[i].x=10;
Graph[i].y=50*(i+1);
}
if(i%3==2||i==2)
{
outtextxy(200,50*i, Graph[i].data);
Graph[i].x=200;
Graph[i].y=50*i;
}
}
for(i=0;i 13、stedge;
while(p)
{
line(Graph[i].x,Graph[i].y,Graph[p->adjvex].x,Graph[p->adjvex].y);
outtextxy((Graph[i].x+Graph[p->adjvex].x)/2,(Graph[i].y+Graph[p->adjvex].y)/2,p->w+48);
arw[0]=Graph[p->adjvex].x+5;
arw[1]=Graph[p->adjvex].y-10;
arw[2]=Graph[p->adjvex].x;
arw[3]=Graph[p->a 14、djvex].y;
arw[4]=Graph[p->adjvex].x+5;
arw[5]=Graph[p->adjvex].y+10;
drawpoly(3,arw);
p=p->nextedge;
}
}
getch();
closegraph();
}
求解关键路径函数:
int SearchMapPath(vexnode* Graph,int vexnumber,int arcnumber)
{
int totaltime=0;
int m=0;
int i,j,k,t;
char s 15、v[100];
int front,rear;
int *topology_queue,*vl,*ve,*el,*ee;
front=rear=-1; t=0;
topology_queue=(int*)malloc(vexnumber*sizeof(int));
vl=(int*)malloc(vexnumber*sizeof(int));
ve=(int*)malloc(vexnumber*sizeof(int));
16、 el=(int*)malloc(arcnumber*sizeof(int));
ee=(int*)malloc(arcnumber*sizeof(int));
edgenode *p;
for(i=0;i 17、 m++;
}
}
while(front!=rear)
{
front++;
j=topology_queue[front];
m++;
p=Graph[j].firstedge ;
while(p)
{
k=p->adjvex;
Graph[k].id --;
if(ve[j]+p->w >ve[k])
ve[k]=ve[j]+p->w ;
18、 if(Graph[k].id ==0) topology_queue[++rear]=k;
p=p->nextedge ;
}
}
if(m 19、vexnumber-2;i>=0;i--)
{
j=topology_queue[i];
p=Graph[j].firstedge;
while(p)
{
k=p->adjvex ;
if((vl[k]-p->w ) 20、 printf("| 起点 | 终点 | 最早开始时间 | 最迟开始时间 | 差值 | \n"); i=0;
for(j=0;j 21、ta ,Graph[k].data ,ee[i],el[i],el[i]-ee[i]);
if(el[i]==ee[i])
{
printf(" 是关键活动 ");
sv[t]=Graph[j].data;t++;
}
printf("\n");
p=p->nextedge;
}
}
printf("关键路径为:");
sv[t]=Graph[vexnumber-1].data;
for(i=0;i<=t;i++) 22、
{ printf("%c",sv[i]);
if(sv[i]!=Graph[vexnumber-1].data)
printf("→");
}
printf("\n");
return 1;
}
3. 主要函数的程序流程图:
4. 画出函数之间的调用关系图:
四、 调试分析
1.实际完成的情况说明:
本程序完成的功能是:显示用户从键盘输入的有向图,并计算出其关键路径。支持用户输入char型的顶点值,int型的弧数和顶点数,int型的权值;
2.程序的性能分析:
本程序的时间复杂度为O(n), 23、空间复杂度为O(1)。
3.上机过程中出现的问题及其解决方案:
(1)有向图的存储。存储时,由于对邻接表的具体代码实现不了解,导致在写这部分程序的过程中遇到不少麻烦,比如如何在结点里添加指向下一邻接结点的指针等,后来查阅了教材,解决了这一部分的问题。
(2)有向图的显示。如何在屏幕上以图形的形式输出图,困惑了我好久。通过在网上查找资料及寻找帮助,明白了需在头文件里添加graphics.h文件,并使用该库里的line()函数和outtextxy()函数。接着是如何显示带箭头的直线,由于graphics.h库里不含有画带箭头的直线的函数,于是,我使用了画多边形的函数drawpoly()来实现 24、箭头,但是这个函数的第二个形参类型是数组型的,需用数组来存储三个点的坐标信息。
(3)关键路径的求解。由于对关键路径的基本算法不了解,所以特地查阅了这一方面的书籍,了解了关键路径的基本算法。
4.程序中可以改进的地方说明:
有向图的显示里,箭头无法随直线的斜率的变化而变化,顶点值与箭头重合,看不清箭头的方向。
5.程序中可以扩充的功能及设计实现假想:
应能显示多个有向图,能求解多个有向图的关键路径,关键活动。界面可以做成可视化,人性化的界面。
五、 测试结果
(1)输入一组可以构成AOE网的顶点与弧的数据
(2) 输入一组不可构成AOE网的顶点与弧的数据 25、即存在回路)
六、 用户手册
(1) 输入图中的节点数与弧数 (2)输入图中各个顶点的值
(3)输入图中弧的权值、起点、终点 (4)按回车键
(5) 按回车键 (6)按任意键
七、体会与自我评价
通过本次课程设,掌握了邻接表的存储结构,图形显示的一些函数的使用以及关键路径的求法。在实验过程中出现了不少的问题,通过查阅资料,向老师、同学寻求帮助,解决了出现的问题。
从这次的课程设计任务中我深刻地体会到:任何事情并不像我们想的那样艰难。刚开始拿到这个课设任务时,对于关键路径很是发怵,因为 26、之前对这一部分的知识运用的少,再加上任务书上还要求以图形的形式显示有向图,在这次课设之前,我从来没有接触过图形处理的函数。然而,俗话说“世上无难事,只怕有心人”,通过积极地查阅书籍,上网查找资料,这些困难都一个个的迎刃而解。并且在解决这些困难的同时,自己也复习并掌握了这些知识。本次课设遇到了很多理论课上所没有遇到过的问题,也让理论课上所学的理论得到验证和使用,通过实际操作,扎实了理论知识,开拓了自己的算法思想。通过本次课设,更让我意识到团队合作的力量,通过小组成员的讨论和互相解惑,学到很多自己没有遇到过的问题的解决办法,学会了如何在团队合作中互帮互助,共同进步。
但是自己的程序仍存在一些不足,比如在有向图的显示方面,箭头与顶点值重合了,导致无法清晰辨认弧的方向。这次课设让我明白了只有扎扎时时的学习知识才能解决实际生活中的实际问题,以后要多了解相关知识,更多的做一些实践动手活动,将知识运用到实际问题中。同时,增加自己的动手能力,这样才能便于我们更好的解决问题。为今后打下好的基础。课外时间应该多扩展一些编程方面的知识,提高自己的编程能力。
八、 参考文献
[1]严蔚敏,数据结构(C语言),清华大学出版社,2007
[2]邱建华,C语言程序设计教程,东软电子出版社,2009
[3]刘汝佳,算法竞赛入门经典,清华大学出版社,2009






