HUNANUNIVERSITY实验五最终报告题目:教学计划编制问题学生姓名学生学号专业班级指导老师完成日期2014年5月15日一、需求分析1.输入形式:用户通过键盘输入课程总数、每门课的课程编号(固定占3位的字母数字串)和直接先修的课程号等的参数。不对非法输入做处理,假定输入的数据都合法。2.输出形式:如果拓扑排序成功,输出拓扑排序后的教学计划编制的顺序;如果拓扑排序不成功,输出排序错误信息,结束程序。3.程序功能:对于用户输入的一组课程编号,根据输入的先修顺序创建邻接矩阵进行存储,并输出拓扑排序后的课程编号的顺序。4.测试数据输入:输入课程总数:3输入每门课的课程编号:A01是否有直接先修的课程(T/F):F输入每门课的课程编号:A02是否有直接先修的课程(T/F):T先修课程编号:A01是否有直接先修的课程(T/F):F输入每门课的课程编号:A03是否有直接先修的课程(T/F):T先修课程编号:A02是否有直接先修的课程(T/F):F输出:教学计划编制完成,课程修读顺序为:A01,A02,A03(输入有误)课程输入错误!教学计划编制失败,请重新输入。二、概要设计抽象数据类型题设要求使用一个有向图表示教学计划,顶点表示某门课程,有向边表示课程之间的先修关系,数据的对象是图中的每一个顶点和有向边。由此为本问题确定一个图的数据关系。拓扑排序可以用顶点入度为0的方法实现,所以为实现拓扑排序的顶点顺序的存放,创建一个队列来存放。图的ADT数据对象:V,R(分别代表某门课程的顶点组成的一个顶点集V和代表课程先修关系的有向弧边组成的一个弧集R。)数据关系:VR={v,w|v,w∈V且P(v,w)}v,w表示从v到w的一条弧,并称v为弧头,w为弧尾。基本操作:intn();//返回图中的顶点数intfirst(int);//返回该点的第一条邻边intnext(int);//返回该店的下一条邻边voidsetEdge(int,int,int);//为有向边设置权值intgetMark(int);//获得顶点的标志值voidsetMark(int);//为顶点设置标志值队列ADT数据对象:int数据关系:R={ai-1,ai|ai-1,ai∈car,i=1,2,3….n}约定a1为队列头,an为队列尾。基本操作:queue();//队列结构初始化~queue();//结构销毁操作boolpush(constint&it);//数据入列boolpop(int&it);//数据出列intsize();//获取队列长度算法的基本思想通过用户输入的顶点的个数(课程数)初始化一个表示有向图的相邻矩阵,课程编号作为相邻矩阵的行列值以及有向边的关系(课程先修关系)完成一个有向图的构建。为了检验图中顶点是否都经过拓扑排序,为每个顶点初始化一个标志值0,当一个顶点经过拓扑排序后更改该顶点标志值0。对相邻矩阵棕的顶点进行入度为0的方法进行拓扑排序。排序结束后,遍历一次图中所有顶点的标志值,当有一个标志值为0时,输出错误信息,结束程序。否则,排序成功,输出排序后的顶点序列。程序的流程(1)初始化模块:输入课程总数,再输入相应数量的课程编号及每个课程的先修课程,用这些信息初始化一个有向图。(2)拓扑排序模块:对有向图进行拓扑排序。(3)输出模块:根据有向图是否为空输出。为空时,输出拓扑排序结果;不为空时输出输入错误提示。各层次模块之间的调用关系三、详细设计物理数据类型由于用户输入的课程个数不定,所以存储拓扑排序后的顶点的个数不定,由此用链式队列来存储排序后的顶点。为了检查图中是否有回路,把每一个顶点的标志值初始化为0。(一)有向图的基本操作1.初始化一个有向图Graphm(intnumVert){inti,j;numVertex=numVert;//顶点数numEdge=0;mark=newint[numVert];//初始化标志数组for(i=0;inumVertex;i++)输出模块拓扑排序模块初始化模块mark[i]=0;//每一个顶点的标志值初始化为0matrix=(int**)newint*[numVertex];for(i=0;inumVertex;i++)matrix[i]=newint[numVertex];//构建一个相邻矩阵for(i=0;inumVertex;i++)for(j=0;jnumVertex;j++)matrix[i][j]=0;}2.有向图的销毁~Graphm(){delete[]mark;for(inti=0;inumVertex;i++)delete[]matrix[i];delete[]matrix;//销毁相邻矩阵}3.获取第一个邻居intfirst(intv)//返回该点的第一条邻边{inti;for(i=0;inumVertex;i++)if(matrix[v][i]!=0)returni;//当顶点和顶点i有边时,返回顶点i的值returni;}intnext(intv1,intv2)//获得v1的邻居v2{inti;for(i=v2+1;inumVertex;i++)if(matrix[v1][i]!=0)returni;returni;}4.其他基本操作voidsetEdge(intv1,intv2)//设置有向图的边{if(matrix[v1][v2]==0)numEdge++;matrix[v1][v2]=1;}intgetMark(intv)//获取顶点标记的值{returnmark[v];}intsetMark(intv,intval)//设置访问的标记{mark[v]=val;}(二)拓扑排序找到第一个入度为0的点存入队列中,从有向图中删去此顶点以及所有以它为尾的弧,再在这些点中找入度为0的点。重复上述操作,直至图空,或者图不空但找不到无前驱的顶点为止,此时返回该队列。queueinttopsort(GraphmG,queueintQ,queueintL,intn){intcount[100];intv,w,i;for(v=0;vn;v++){count[v]=0;}for(v=0;vn;v++)for(w=G.first(v);wn;w=G.next(v,w))count[w]++;for(v=0;vn;v++)if(count[v]==0)//找到度为0的点{Q.push(v);G.setMark(v,1);}//顶点进队列,并更改顶点标志值为1while(Q.size()!=0){i=Q.front();Q.pop();L.push(i);for(w=G.first(i);wn;w=G.next(i,w)){count[w]--;//顶点度减一if(count[w]==0)//找到度为0的点{Q.push(w);G.setMark(w,1);}//顶点进队列,并更改顶点标志值为1}}returnL;//返回存放排序后顶点的队列}(三)队列基本操作//压入队列boolpop(char*&it){if(length()==0)returnfalse;it=front-elem;qnode*ltemp=front;front=front-next;deleteltemp;if(front==NULL)rear=NULL;size--;returntrue;}//出队列boolpush(constchar*&it){if(rear==NULL)front=rear=newqnode(it,NULL);else//append{rear-next=newqnode(it,NULL);rear=rear-next;}size++;returntrue;}//获取队列长度intsize()const{returnsize;}最后,判断图中是否有回路。可以通过遍历图中的每一个顶点的标记值,如果有一个为0,那么说明图中存在回路。for(i=0;in;i++){if(G.getMark(i)==0)//为0时表示该顶点未经过拓扑排序{cout课程输入错误!教学计划编制失败,请重新输入。endl;exit(0);}}算法的具体步骤创建一个数组存储顶点信息,再构建一个邻接矩阵存储输入的课程编号(顶点),和课程先修关系(有向边)构成的有向图的信息,然后对邻接矩阵中的图的信息进行拓扑排序,把排序结果存放在一个队列中。如果一次排序结束后,遍历顶点标志值有为0,输出输入错误提示,结束程序;否则,输出队列中存储的课程编号序列。流程图如下:输入顶点数nin?输入课程编号get(v1);strcpy(v,v1);i++YNT.CreatGraphm(n)in?Y输入先修关系n2T.setEdge(n2,i);NqueueintS;S=topsort(T);拓扑排序in?T.getMark()==0?NY输出顶点序列错误结束YY伪代码如下,charv[100][5];charv1[4],v2[4];GraphmT;queueintS;cin.get(n);//输入课程总数nT.CreatGraphm(n);cin.get(v);//输入每门课的编号,保存在*v[4]数组中for(i=0;in;i++){coutv[i]---是否有直接先修的课程(T/F):;cinch;while(ch=='T'){GetNum(n2);//输入先修课程编号T.setEdge(n2,i);//n2在前表示先修的顺序cout是否有直接先修的课程(T/F):;cinch;}}S=topsort(T,Q,L,n);//对图T进行拓扑排序,排序序列存储在队列中返回到Scout教学计划编制完成,课程修读顺序为:endl;printout(S);//输出排序后的顶点序列}算法的时空分析及改进设想因为图的邻接矩阵是一个|V|×|V|矩阵,所以邻接矩阵的空间代价为Θ(|V|^2),对于有n个顶点的和E条弧的有向图而言,对此图的拓扑排序算法时间复杂度为Θ(V+E)输入和输出的格式输入:1.输入课程数n----cin.get(n);cout输入课程总数:;cinn;2.输入每门课的课程编号----cin.get(v);for(i=0;in;i++){cout输入每门课的课程编号:endl;cin.get();cin.getline(v1,4);strcpy(v[i],v1);//要用字符串拷贝函数,用等号不能正确的赋值!!}3.获得先修的课程编号----GetNum(n2);cout先修课程编号:;cin.get();in.getline(v2,4);n2=getNum(v,n,v2);输出:1.编制成功,把队列S中的顶点序列输出。----printout(S);for(i=0;in;i++){j=S.front();coutv[j];S.pop();}2.编制失败,图中有回路,输出错误信息,结束程序。if(G.getMark(i)==0)//为0时表示该顶点未经过拓扑排序{cout课程输入错误!教学计划编制失败,请重新输入。endl;exit(0);}四、测试数据编制成功,003-001-002编制失败,001-002,002-001附上完整代码#includequeue#includeiostream.h#includestring.husingnamespacestd;classedge{public:intvertex,weight;//顶点和权edge(){vertex=-1;weight=-1;}edge(intv,intw){vertex=v;weight=w;}};classGraphm{private:intnumVertex,numEdge;int**matrix;//矩阵int*mark;public:Graphm(){}voidCreatGraphm(intnumV