数据结构实验五教学计划编制课案.docx
《数据结构实验五教学计划编制课案.docx》由会员分享,可在线阅读,更多相关《数据结构实验五教学计划编制课案.docx(20页珍藏版)》请在冰豆网上搜索。
![数据结构实验五教学计划编制课案.docx](https://file1.bdocx.com/fileroot1/2022-12/30/668ccf79-b1fd-4387-9dc0-b3a27e046844/668ccf79-b1fd-4387-9dc0-b3a27e0468441.gif)
数据结构实验五教学计划编制课案
HUNANUNIVERSITY
实验五最终报告
题目教学计划编制问题
学生姓名
学生学号
专业班级
指导老师李晓鸿
完成日期2015年12月17日
一.需求分析
1.程序的功能
用户通过键盘输入课程总数、每门课的课程编号和直接先修的课程号等的参数。
用有向网表示教学计划,其中顶点表示某门课程,有向边表示课程之间的先修关系(如果A课程是B课程的先修课程,那么A到B之间有一条有向边从A指向B)。
最终输出一个不冲突的线性的课程教学流程。
2.输入形式
请输入课程总数:
//输入一个正整数n
请输入课程1的名称:
//输入任意字符串
请输入课程2的名称:
..............
请输入课程n的名称:
(课程1)否是有先修(1/0):
//输入1表示有,0表示没有
若输入1:
请输入先修课程的名称:
//输入存在的先修课程名称
...............
(课程n)否是有先修(1/0):
请输入先修课程的名称:
3.输出形式
输出成功:
课程表的排序为:
//输出没有冲突的课表排序
输出失败:
课程输入错误!
教学计划编制失败,请重新输入。
4.测试数据:
①.正常的输入
输入:
输入课程总数:
3
请输入课程1的名称:
小学数学
请输入课程2的名称:
初中数学
请输入课程3的名称:
大学数学
小学数学是否有先修(1/0):
0
初中数学是否有先修(1/0):
1
请输入先修课程的名称:
小学数学
高中数学是否有先修(1/0):
1
请输入先修课程的名称:
初中数学
输出:
课程表的排序为:
小学数学初中数学高中数学
②.正常的输入
输入:
输入课程总数:
4
请输入课程1的名称:
1
请输入课程2的名称:
2
请输入课程3的名称:
3
请输入课程4的名称:
4
1是否有先修(1/0):
0
请输入先修课程的名称:
4
2是否有先修(1/0):
1
请输入先修课程的名称:
3
3是否有先修(1/0):
1
请输入先修课程的名称:
4
4是否有先修(1/0):
0
输出:
课程表的排序为:
4132
③.有两个先修课程的情况
输入:
输入课程总数:
4
请输入课程1的名称:
A
请输入课程2的名称:
B
请输入课程3的名称:
C
请输入课程4的名称:
D
A是否有先修(1/0):
0
B是否有先修(1/0):
1
请输入先修课程的名称:
A
C是否有先修(1/0):
1
请输入先修课程的名称:
A
D是否有先修(1/0):
1
请输入先修课程的名称:
C
输出:
课程表的排序为:
ABCD
④.有三个先修课程的情况
输入:
输入课程总数:
4
请输入课程1的名称:
A
请输入课程2的名称:
B
请输入课程3的名称:
C
请输入课程4的名称:
D
A是否有先修(1/0):
1
请输入先修课程的名称:
D
B是否有先修(1/0):
1
请输入先修课程的名称:
D
C是否有先修(1/0):
1
请输入先修课程的名称:
D
D是否有先修(1/0):
0
输出:
课程表的排序为:
DABC
⑤.所有课程无先修
输入:
输入课程总数:
3
请输入课程1的名称:
1
请输入课程2的名称:
2
请输入课程3的名称:
3
A是否有先修(1/0):
0
B是否有先修(1/0):
0
C是否有先修(1/0):
0
输出:
课程表的排序为:
123
二、概要设计
1.抽象数据类型
题设要求使用一个有向图表示教学计划,顶点表示某门课程,有向边表示课程之间的先修关系,数据的对象是图中的每一个顶点和有向边。
由此为本问题确定一个图的数据关系。
本题目需要一个数据结构来储存遍历过的图的结点,该数据结构满足先进先出,所以用队列来实现。
2.ADT
ADTEdge{
数据对象:
N(边的名称)V(标记)F(先修课结点)
数据关系:
(N&&V&&F)∈R
(R1&&R2&&Rn)∈Graph
基本操作:
stringgetVal(intv)//返回边的名称
intgetMark(intv)//返回标记
voidsetVal(intv,stringval)//设边的名称
voidsetMark(intv,intMark)//设置标记
voidsetfirst(intv)//设置先修结点标记
}
ADTGraph{
数据对象:
V,R(分别代表某门课程的顶点组成的一个顶点集 V 和代表课程先修关系的有向弧边组成的一个弧集 R。
)
数据关系:
VR={|v,w∈V且P(v,w)}
表示从v到w的一条弧,并称v为弧头,w为弧尾。
基本操作:
intn();//返回图中的顶点数
intfirst(int);//返回该点的第一条邻边
intnext(int);//返回该店的下一条邻边
voidsetEdge(int,int,int);//为有向边设置权值
voidFind(stringsearch,int&v)
intn()
}
ADTqueue{
数据对象:
前指针front,后指针rear
数据关系:
R={|ai-1,ai∈car,i=1,2,3….n}
约定a1为队列头,an为队列尾。
基本操作:
queue();//队列结构初始化
~queue();//结构销毁操作
boolpush(constint&it);//数据入列
boolpop(int&it);//数据出列
intsize();//获取队列长度}
3.算法的基本思想
①.通过用户输入的顶点的个数(课程数)初始化一个表示有向图的相邻矩阵,初始化边的访问次数全部设置为零,通过输入边的信息和先修关系,设置先修关系的计数器,记录每条边先修关系的数量,完成对有项图的构建。
②.将先修关系为零的边放入队列,然后开始处理队列。
当从队列中删除一个顶点时,把它打印出来,同时将其所有相邻顶点的先修关系计数器减一。
当某个相邻顶点的计数器为0时,就将其放入队列。
如果还有顶点未被打印,而队列已经为空,则图中必然包含回路(既不可能不违反先决条件完成任务)。
4.程序的流程
(1)初始化模块:
输入课程总数,再输入相应数量的课程编号及每个课程的先修课程,用这些信息初始化一个有向图。
(2)拓扑排序模块:
对有向图进行拓扑排序。
(3)输出模块:
根据有向图是否为空输出。
为空时,输出拓扑排序结果;不为空时输出输入错误提示。
三、详细设计
1.物理数据类型
用户输入的课程个数不定,所以存储拓扑排序后的顶点的个数不定,对于图有两种存储方式,本题中用邻接矩阵来存储图的信息,对于边很少的图来说,虽然用邻接矩阵有些浪费空间,但是题目做起来相对方便。
由于用户输入的课程个数不定,使用链式栈。
使用string类型储存用户信息和边的信息。
2.算法的具体步骤
初始化一个有向图——先修信息的储存——拓扑排序与输出
①.初始化一个有向图:
包括初始化被访问标记和先修标记,动态创建二维数组和用于储存边信息的一维数组。
Graph(intnumVert)
{
inti,j;
numVertex=numVert;
numEdge=0;
mark=newPoint[numVert];//Initializemarkarray
for(i=0;i{
mark[i].visit=-1;//包括初始化被访问标记和先修标记
mark[i].first=0;
}
matrix=(int**)newint*[numVertex];//Makematrix
for(i=0;imatrix[i]=newint[numVertex];
for(i=0;ifor(intj=0;j}
②.先修信息的存储:
由用户输入是否有先修课程后,用户每输入一个先修课程,就将这个课程对应的先修标记加1.
cout<cin>>judge;
if(judge)
{
a.setfirst(i);
cout<<"请输入先修课程的名称:
";
cin>>Ch1;
a.Find(Ch1,v1);
a.setEdge(v1,i,10);
}
voidsetfirst(intv)
{
mark[v].first++;
}
③.拓扑排序与输出:
定义两个队列A和B,将先修关系为零的边放入队列A,然后开始处理队列。
当从队列A中删除一个顶点时,该顶点进入B队列,再把该顶点打印出来,同时将其所有相邻顶点的先修关系计数器减一。
当某个相邻顶点的计数器为0时,就将其放入队列。
如果还有顶点未被打印,而队列已经为空,则图中必然包含回路(既不可能不违反先决条件完成任务)。
输出:
重复将队列B中首元素输出并删除,直到队列为空,就是课程表的排序。
voidDFS(Graph*G,queue*Q,queue*L)
{
for(intv=0;vif(mark[v].first==0)
{
Q->push(v);
setMark(v,1);
}
while(Q->size()!
=0)
{
inti=Q->front();//获取Q栈首元素
Q->pop();//弹出Q栈
L->push(i);//进L栈
for(intw=first(i);w{
mark[w].first--;
if(mark[w].first==0)
{
Q->push(w);setMark(w,1);
}
}
}
for(inti=0;i{
if(getMark(i)==-1)//为0时表示还未被删除,图不为空
{
cout<<"课程输入错误!
教学计划编制失败,请重新输入。
"<exit(0);
}
}
}
3.算法的时空分析及改进设想
因为图的邻接矩阵是一个|V|×|V|矩阵,所以邻接矩阵的空间代价为Θ(|V|^2),对于有n个顶点的和E条弧的有向图而言,对此图的拓扑排序算法时间复杂度为Θ(V+E)
4.输入和输出的格式
输入:
1.输入课程数n
cout<<"请输入课程总数:
";
cin>>n;
if(n<=0)
{
cout<<"输入错误重新输入(大于零的整数)"<cout<<"请输入课程总数:
";
cin>>n;
}
2.输入每门课的课程编号
for(inti=0;i{
cout<<"请输入课程"<
";
cin>>Ch;
a.setVal(i,Ch);
}
3.获得先修的课程编号
for(inti=0;i{
judge=0;
cout<cin>>judge;
if(judge)
{
a.setfirst(i);
cout<<"请输入先修课程的名称:
";
cin>>Ch1;
a.Find(Ch1,v1);
a.setEdge(v1,i,10);
}
}
输出:
1.编制成功,把队列S中的顶点序列输出。
cout<<"课程表的排序为:
"<a.DFS(&a,&Q,&L);
for(inti=0;i{
intj=L.front();
cout<L.pop();
}
2.编制失败,图中有回路,输出错误信息,结束程序。
if(G.getMark(i)==0)//为0时表示该顶点未经过拓扑排序
{
cout<<"课程输入错误!
教学计划编制失败,请重新输入。
"<exit(0);
}
四.调试分析
DFS问题,书上思路很明确并且有很多源码,没有大的问题。
五.测试结果
1.正常的输入输出
②.正常的输入
3.有两个先修课程的情况
④.有三个先修课程的情况
⑤.所有课程无先修
附录
#include
#include
#include
#include
#include
usingnamespacestd;
classPoint
{
public:
stringLessonName;
intvisit;
intfirst;//1有先修,0无
};
classGraph
{//Implementadjacencymatrix
private:
intnumVertex,numEdge;//Storenumberofverticesedges
int**matrix;//Pointertoadjacencymatrix
Point*mark;//Pointertomarkarray
public:
Graph(intnumVert)
{//Makegraphw/numVertvertices
inti,j;
numVertex=numVert;
numEdge=0;
mark=newPoint[numVert];//Initializemarkarray
for(i=0;i{
mark[i].visit=-1;
mark[i].first=0;
}
matrix=(int**)newint*[numVertex];//Makematrix
for(i=0;imatrix[i]=newint[numVertex];
for(i=0;ifor(intj=0;j}
~Graph()
{
delete[]mark;
for(inti=0;idelete[]matrix[i];
delete[]matrix;
}
intn()
{
returnnumVertex;
}
inte()
{
returnnumEdge;
}
intfirst(intv)
{//Returnv'sfirstneighbor
inti;
for(i=0;iif(matrix[v][i]!
=0&&mark[i].visit==-1)returni;
returni;//Returnnifnone
}
intnext(intv1,intv2)
{
//Getv1'sneighborafterv2
inti;
for(i=v2+1;iif(matrix[v1][i]!
=0&&mark[i].visit==-1)
{
//cout<<"此时next的i值是:
"<returni;
}
returni;
}
//Setedge(v1,v2)towgt
voidsetEdge(intv1,intv2,intwgt)
{
numEdge++;//ERROR
matrix[v1][v2]=wgt;
}
voiddelEdge(intv1,intv2)
{//Deleteedge(v1,v2)
if(matrix[v1][v2]!
=0)
numEdge--;
matrix[v1][v2]=0;
}
intweight(intv1,intv2)
{
returnmatrix[v1][v2];
}
stringgetVal(intv)
{
returnmark[v].LessonName;
}
intgetMark(intv)
{
returnmark[v].visit;
}
voidsetVal(intv,stringval)
{
mark[v].LessonName=val;
}
voidsetMark(intv,intMark)
{
mark[v].visit=Mark;
}
voidsetfirst(intv)
{
mark[v].first++;
}
voidFind(stringsearch,int&v)
{
for(inti=0;i{
if(mark[i].LessonName==search)
{
v=i;
return;
}
}
cout<<"路径错误"<return;
}
voidDFS(Graph*G,queue*Q,queue*L)
{
for(intv=0;vif(mark[v].first==0)
{
Q->push(v);
setMark(v,1);
}
while(Q->size()!
=0)
{
inti=Q->front();//获取Q栈首元素
Q->pop();//弹出Q栈
L->push(i);//进L栈
for(intw=first(i);w{
mark[w].first--;
if(mark[w].first==0)
{
Q->push(w);setMark(w,1);
}
}
}
for(inti=0;i{
if(getMark(i)==-1)//为0时表示还未被删除,图不为空
{
cout<<"课程输入错误!
教学计划编制失败,请重新输入。
"<exit(0);
}
}
}
};
intmain()
{
intn;
intv1;
intv2;
intjudge;
stringCh;
stringCh1;
stringCh2;
queueQ;
queueL;
intD[100];
intcount=0;
cout<<"请输入课程总数:
";
cin>>n;
if(n<=0)
{
cout<<"输入错误重新输入(大于零的整数)"<cout<<"请输入课程总数:
";
cin>>n;
}
Grapha(n);
for(inti=0;i{
cout<<"请输入课程"<
";
cin>>Ch;
a.setVal(i,Ch);
}
for(inti=0;i{
judge=0;
cout<cin>>judge;
if(judge)
{
a.setfirst(i);
cout<<"请输入先修课程的名称:
";
cin>>Ch1;
a.Find(Ch1,v1);
a.setEdge(v1,i,10);
}
}
cout<<"课程表的排序为:
"<a.DFS(&a,&Q,&L);
for(inti=0;i{
intj=L.front();
cout<L.pop();
}
system("pause");
return0;
}