关键路径算法课程设计.docx
《关键路径算法课程设计.docx》由会员分享,可在线阅读,更多相关《关键路径算法课程设计.docx(22页珍藏版)》请在冰豆网上搜索。
![关键路径算法课程设计.docx](https://file1.bdocx.com/fileroot1/2023-7/10/130bb203-0e2a-44c2-a4c5-1b67bf0ced38/130bb203-0e2a-44c2-a4c5-1b67bf0ced381.gif)
关键路径算法课程设计
《数据结构》课程设计说明书
题目:
求网中的关键路径
班级
计科1203
组别:
指导老师:
完成时间:
组长:
学号:
组员1:
学号:
组员2:
学号:
组员3:
学号:
成绩:
目录
一需求分析1
1.1题目内容与要求1
1.2题目理解与功能分析1
二概要设计2
2.1设计思路2
2.2系统模块图2
2.2系统模块图……………………………………………………….3
三详细设计……………………………………………….…4
3.1图存储结构的建立………………………..……………………4
3.2求取关键路径……………………………………………………..8
3.3主程序建立…………………………………………………10
四实验结果…………………………………………………….11
五课程设计心得…………………………………………………...12
六参考文献……………………………………………………...13
七附录(程序清单)……………………………………………14
一需求分析
1.1题目内容与要求
内容:
自拟定合适的方式,从键盘上输入一个AOE网,并用合适的存储结构存储该AOE网,然后求出该AOE网的关键路径。
基本要求:
1.输入AOE网的方式要尽量的简单方便;
2.要能够较形象地观察AOE网和它的关键路径;
3.课程设计报告必须符合课程设计报告规范;
4.提交合格的课程设计报告,经指导教师测试课设完成(验收)程序,课设完成;
1.2题目理解与功能分析
该题实质要求用数据结构中的图形知识编写一个求无循环有向帯权图中从起点到终点所有路径,经分析、比较求出长度最大路径,从而求出关键路径。
通常我们用有向图表示一个工程。
在这种有向图中,用顶点表示活动,用有向边表示活动Vi必须先于活动Vj进行。
如果在这种图中用有向边表示一个工程中的各项活动(ACTIVITY),用有向边上的权值表示活动的持续时间(DURATION),用顶点表示事件(EVENT),则这种的有向图叫做用边表示活动的网络,简称AOE网络。
在AOE网络中,从源点到各个顶点,可能不止一条。
这些路径的长度也可能不同。
不同路径所需的时间虽然不同,但只有各条路径上所有活动都完成了,这个工程才算完成。
因此,完成整个工程所需的时间取决于从源点到汇点的最长路径长度,即在这条路径上所有活动的持续时间之和。
这条路径长度就叫做关键路径(criticalpath)。
程序所要达到的功能:
输入并建立AOE网;输出关键活动并求出这个工程的关键路径;求出完成这个关键路径的最少时间并输出,该程序结束。
二概要设计
2.1设计思路
基本设计思路:
(1)以某一个求无循环有向帯权图蓝本。
(2)观察并记录这个图中每个弧的起始点及权值。
(3)用记录的结果建立AOE网,即边表示活动的网络,并用图的形式表示。
(4)用领接表来存储图这些信息。
(5)用CreateGraph()函数建立AOE图。
(6)用CriticalPath()函数求出最大路径,并打印出关键路径。
(7)编写代码并测试。
2.2系统模块图
图2-1系统模块图
三详细设计
3.1图存储结构的建立
全局变量及用途:
#defineMAX_VERTEX_NUM50//最大顶点数
intindegree[MAX_VERTEX_NUM];//存放节点入度数组
intve[MAX_VERTEX_NUM],vl[MAX_VERTEX_NUM];//顶点事件最早发生时间和最迟发生时间数组,全局变量初始为0
intee[MAX_VERTEX_NUM],el[MAX_VERTEX_NUM];//弧活动最早发生时间和最迟发生时间数组
stackS;//存放入度为0的结点
stackT;//存放顶点的拓扑序列
1先建立邻接表的存储单元,为建立邻接表做准备。
为图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对于有向图是以vi为尾的弧)。
每个结点由3个域组成,其中邻接域(adjvex)指示与顶点vi邻接的点在图中的位置,链域(nextedge)指示下一条边或弧的结点,权值域(W)存储边或弧的权值大小。
在表头结点除了设有链域(firstedge)指向链表中第一个结点之外,还设有存储顶点v或其他有关的数据域(data)和存储顶点入度的域(id)(代码如下)。
数据存储结构:
typedefstructArcNode//结点结构
{
intadjvex;//该边所指的顶点的位置
structArcNode*nextarc;//指向下一条边的指针
InforTypeinfo;//弧的长度(关键路径中的活动)
}ArcNode;//表的结点
typedefstructVNode//顶点
{
VertexTypedata;//顶点信息【如数据等】
ArcNode*firstarc;//指向第一条依附该顶点的边的弧指针
}VNode;
typedefstructALGraph
{
VNodevertices[MAX_VERTEX_NUM];//顶点线性表【数组】
intvexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;
2构造有向图。
构图函数由增加结点和增加边构成。
第一,增加结点:
输入顶点数级定点信息,并初始化该顶点的边表。
第二,首先输入边所依附的两个顶点及权重,最后将该结点接到第i个表尾部。
(代码如下)
定位函数:
//==========返回顶点v在顶点向量中的位置===========
intLocateVex(ALGraph*G,charv)
{
for(inti=1;v!
=G->vertices[i].data&&i<=G->vexnum;i++)
if(i>G->vexnum)
return0;
returni;
}
//===========构造邻接链表图==================
voidCreateGraph(ALGraph*G)
{
add_vex(G);//增加节点
add_arc(G);//增加边
}
//==============增加边=====================
voidadd_arc(ALGraph*G)
{
ArcNode*s;
ArcNode*p;
cout<<"\n输入有向图边数:
";
cin>>G->arcnum;
charv1,v2;
intlength;
cout<<"\n\n输入边信息(始点终点权值):
"<for(intk=1;k<=G->arcnum;k++)
{
cin>>v1>>v2>>length;
inti=LocateVex(G,v1);
intj=LocateVex(G,v2);//确定v1,v2在G中的位置
indegree[j]++;//点j的入度增加1
s=(ArcNode*)malloc(sizeof(ArcNode));
s->adjvex=j;//该边所指向的顶点的位置为j
s->info=length;
s->nextarc=NULL;
if(G->vertices[i].firstarc==NULL)
{
G->vertices[i].firstarc=s;
}
else
{
for(p=G->vertices[i].firstarc;p->nextarc!
=NULL;p=p->nextarc)//尾插法构建顶点链表;
p->nextarc=s;
}
}
}
3.2求取关键路径
利用AOE网进行工程管理时,需解决的两个主要问题:
其一,计算完成整个工程的最短工期;其二,确定关键路径,以找出哪些活动时影响工程进度的关键。
因此须计算以下几点:
(1)事件的最早发生时间ve[k];
(2)事件最迟发生时间vl[k];
(3)活动最早开始时间ee[i];
(4)活动的最迟开始时间el[i];
计算其过程必须分别在拓扑有序和逆拓扑有序的前提下进行。
也就说,ve[k]必须在事件vk所有前驱的最早发生的时间求得之后才能确定。
因此,可以在拓扑排序的基础上计算ve[k]和vl[k]。
由此得到求解关键路径的方法:
首先输入arcnum条有向边,建立AOE网的邻接表存储结构;然后从始点出发,令事件的最早发生时间为0,按拓扑有序求其余各顶点时间的最早发生时间ve[k];(代码如下)
If(ve[t]+p->info>ve[k])ve[k]=ve[t]+p->info;
//ve[k]k(终点)顶点事件最早能发生的时间ve[]初始为0
接着从终点出发,令事件最迟发生时间等于其最早发生时间,按你你逆拓扑排序求其余各顶点事件最迟发生时间vl[k];最后根据各顶点事件的ve和vl值,求所有活动最早开始时间ee和最迟开始时间el。
如果某活动满足条件ee=el,则为关键活动。
(代码如下)
if(vl[k]-dut//vl[t]t(始点)顶点事件最迟发生时间
for(intj=1;j<=G->vexnum;j++)
{p=G->vertices[j].firstarc;
while(p)
{i++;
intk=p->adjvex;
ee[i]=ve[j];
el[i]=vl[k]-p->info;
printf("|%3c|%3c|%6d|%6d|%3d|",G->vertices[j].data,G->vertices[k].data,ee[i],el[i],el[i]-ee[i]);
if(el[i]==ee[i]){printf("此弧为关键活动");}
p=p->nextarc;
}
}
同时,为计算各顶点事件的ve值是在拓扑排序的过程中进行的,因此需一个栈来记录拓扑排序,如果顶点的入度为0,则该顶点入栈。
intt=S.top();
S.pop();T.push(t);count++;
//度为0的结点出栈S入栈T栈T保存拓扑序列
cout<<"关键路径所需时间为:
"<vexnum];
3.3主程序建立
该部分主要是对所建立的函数的调用。
包括:
建立图的函数CreateGraph();
计算关键路径的函数CriticalPath();
最后程序结束。
这样安排可以增强程序的可读性,是程序便于理解,也便于日后的对程序的维护和修改等操作。
四实验结果
按照要求输入一组关于无循环有向帯权图所有信息。
如:
6123456812301320242025303440363046205610
依次执行程序每一步,最后结束该程序。
程序运行如下图:
五程序设计心得
通过这次求关键路径,我对图的邻接表有了深刻的了解。
以前都是用邻接矩阵的,感觉邻接矩阵直观简便,而这次关键路径的有关算法,书上都是使用邻接表的,我试着熟悉它,发现也挺好用的,许多操作用邻接表更方便。
书上没有邻接表的构图代码,拓扑排序,关键路径的都是伪代码,我不得不去查资料,思索,自己编写完整可执行的代码,这个过程很艰辛,也很愉快。
拓扑排序时我想办法弄出了不同拓扑排序的总数,却怎么也无法打印出所有的拓扑序列。
在关键路径求顶点事件最迟的发生时间时,错把各个顶点的最早发生时间赋给了它,所以输出的结果怎么都不对,我在这个地方纠结的好久。
知识只有在使用的时候才会远远感觉不够用,我深深体会到了这句话。
这次的程序设计对我的帮助很大,虽然真正自己写的代码并不多,许多都是书上的代码经过补充改写的,但毕竟我都把这整个过程走了一遍,熟悉了图的各种操作,各种算法我都认真看懂了,思考了。
我能感觉到自己的进步,每次编写出一个程序,在电脑上运行出来时,都会有一种莫名的成就感,很享受这种感觉,我会继续努力的。
----------计科1203王勋峰
六参考文献
[1]严蔚敏编。
数据结构(C语言版)。
北京:
清华大学出版社,1997.2
[2]谭浩强著。
C语言程序设计(第二版)。
北京:
清华大出版社,2001.3
[3]武爱平编。
C语言程序设计。
长春:
吉林大学出版社出版,2010.1
七附录(程序清单)
程序可运行
#include
usingnamespacestd;
#include
typedefcharVertexType;
typedefintVRType;
typedefintInforType;
#defineMAX_VERTEX_NUM50//最大顶点数
intindegree[MAX_VERTEX_NUM];//存放节点入度数组
intve[MAX_VERTEX_NUM],vl[MAX_VERTEX_NUM];//顶点事件最早发生时间和最迟发生时间数组,全局变量初始为0
intee[MAX_VERTEX_NUM],el[MAX_VERTEX_NUM];//弧活动最早发生时间和最迟发生时间数组
stackS;//存放入度为0的结点
stackT;//存放顶点的拓扑序列
/***********************************************************/
typedefstructArcNode//结点结构
{
intadjvex;//该边所指的顶点的位置
structArcNode*nextarc;//指向下一条边的指针
InforTypeinfo;//弧的长度(关键路径中的活动)
}ArcNode;//表的结点
typedefstructVNode//顶点
{
VertexTypedata;//顶点信息【如数据等】
ArcNode*firstarc;//指向第一条依附该顶点的边的弧指针
}VNode;
typedefstructALGraph
{
VNodevertices[50];//顶点线性表【数组】
intvexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;
/*************************************************************/
//==========返回顶点v在顶点向量中的位置===========
intLocateVex(ALGraph*G,charv)
{
for(inti=1;v!
=G->vertices[i].data&&i<=G->vexnum;i++)
;
if(i>G->vexnum)
return0;
returni;
}
//==============增加节点====================
voidadd_vex(ALGraph*G)
{
cout<<"\n输入有向图顶点数:
";
cin>>G->vexnum;
cout<<"\n输入顶点信息:
"<for(inti=1;i<=G->vexnum;i++)
{
cin>>G->vertices[i].data;//构造顶点向量
G->vertices[i].firstarc=NULL;//初始链指针
}
}
//==============增加边=====================
voidadd_arc(ALGraph*G)
{
ArcNode*s;
ArcNode*p;
cout<<"\n输入有向图边数:
";
cin>>G->arcnum;
charv1,v2;
intlength;
cout<<"\n\n输入边信息(始点终点权值):
"<for(intk=1;k<=G->arcnum;k++)
{
cin>>v1>>v2>>length;
inti=LocateVex(G,v1);
intj=LocateVex(G,v2);//确定v1,v2在G中的位置
indegree[j]++;//点j的入度增加1
s=(ArcNode*)malloc(sizeof(ArcNode));
s->adjvex=j;//该边所指向的顶点的位置为j
s->info=length;
s->nextarc=NULL;
if(G->vertices[i].firstarc==NULL)
{
G->vertices[i].firstarc=s;
}
else
{
for(p=G->vertices[i].firstarc;p->nextarc!
=NULL;p=p->nextarc)//尾插法构建顶点链表
;
p->nextarc=s;
}
}
}
//===========构造邻接链表图==================
voidCreateGraph(ALGraph*G)
{
add_vex(G);//增加节点
add_arc(G);//增加边
}
//================拓扑排序=====================
intTopologicalSort(ALGraph*G)
{
inta[10];
intcount=0;//count为入栈【访问】顶点的计数
ArcNode*p;
for(inti=1;i<=G->vexnum;i++)
{
if(indegree[i]==0)
S.push(i);
}
printf("\n拓扑排序:
\n");
i=1;
while(!
S.empty())
{
intt=S.top();
a[0]=1;
a[i]=a[i-1]*S.size();
printf("\n第%d步有(%d)种方式",i,S.size());//栈内为度为0结点的【下标】
i++;
printf("-->%c(%d)",G->vertices[t].data,t);
S.pop();T.push(t);count++;//度为0的结点出栈S入栈T栈T保存拓扑序列
for(p=G->vertices[t].firstarc;p;p=p->nextarc)//与度为0结点相邻的结点的入度减1
{
intk=p->adjvex;
indegree[k]--;
if(indegree[k]==0)S.push(k);
if(ve[t]+p->info>ve[k])ve[k]=ve[t]+p->info;//ve[k]k(终点)顶点事件最早能发生的时间ve[]初始为0
}
}
printf("\n\n共有%d种拓扑排序方式\n",a[G->vexnum]);
if(countvexnum)return0;//若小于顶点数说明图中有环
elsereturn1;
}
//=================求关键路径==========================
voidCriticalPath(ALGraph*G)//G为有向网,输出G的各项关键活动
{
ArcNode*p;
if(TopologicalSort(G)==0)
{
cout<<"\n该图有环!
";
}
for(inti=1;i<=G->vexnum;i++)//初始化顶点事件最初发生的事件为工程总时间(为一较大值)
{
vl[i]=ve[G->vexnum];
}
printf("\n逆拓扑序列为:
");
while(!
T.empty())
{
intt=T.top();
printf("--->%c",G->vertices[t].data);
T.pop();
for(p=G->vertices[t].firstarc;p!
=NULL;p=p->nextarc)
{
intk=p->adjvex;
intdut=p->info;
if(vl[k]-dut}
}
printf("\n\n\n|起点|终点|最早开始时间|最迟开始时间|差值|是否为关键路径\n\n");
i=0;
for(intj=1;j<=G->vexnum;j++)
{
p=G->vertices[j].firstarc;
while(p)
{
i++;