HUNAN UNIVERSITY 课程实习报告题目教学计划编制问题学生姓名学生学号专业班级指导老师李晓鸿完成日期2014年12月16日一、需求分析1.问题描述:用有向网表示教学计划,其中顶点表示某门课程,有向边表示课程之间的先修关系(如果A课程是B课程的先修课程,那么A到B之间有一条有向边从A指向B)。
设计一个教学计划编制程序,获取一个不冲突的线性的课程教学流程。
(课程线性排列,每门课上课时其先修课程已经被安排)。
2.程序功能:本程序要求根据所输入的课程及课程间的先修关系,得到一个不冲突的线性的课程表。
3.输入的形式和输入值的范围用户通过键盘输入课程总数、每门课的课程编号(固定占3位的字母数字串)和直接先修的课程号等的参数。
本程序不对非法输入做处理,即假设输入都是合法的。
4.输出的形式如果排序成功,输出排序后的教学计划表;否则输出错误提示信息,表示所输入的课程不能构成一个完全满足教学要求的课程表。
5.测试数据:输入:请输入课程的个数和课程关系的个数:4 3请输入点,即课程编号1:A1请输入点,即课程编号2:A2请输入点,即课程编号3:A3请输入点,即课程编号4:A4请输入有向边,即课程的先后关系1:A2 A4请输入有向边,即课程的先后关系2:A4 A3请输入有向边,即课程的先后关系3:A3 A1请输入课程的个数和课程关系的个数:3 3请输入点,即课程编号1:A1请输入点,即课程编号2:A2请输入点,即课程编号3:A3请输入有向边,即课程的先后关系1:A2 A1请输入有向边,即课程的先后关系2:A1 A3请输入有向边,即课程的先后关系3:A3 A2输出:课程的选修的先后顺序为:A2 A4 A3 A1课程的选修的先后顺序为:课程网络存在回路二、概要设计1.抽象数据类型的定义:题设要求使用一个有向图表示教学计划,顶点表示某门课程,有向边表示课程之间的先修关系,数据的对象是图中的每一个顶点和有向边。
由此为本问题确定一个图的数据关系。
同时课程存储在顶点位置,所以创建节点类来存储课程信息。
在对图中所存储的课程进行排序时,使用拓扑排序可以完美得到所需顺序,而拓扑排序可以用顶点入度为0的方法实现,所以为实现拓扑排序的顶点的存放,创建一个线性表来存放。
(1)图的ADT数据对象:V :代表表示课程的定点组成的定点集;R :代表表示课程先修关系的有向弧边组成的一个弧集;数据关系:VR={<v,w>| v,w∈V 且 P(v,w)}<v,w>表示从 v 到 w 的一条弧,并称 v 为弧头,w 为弧尾。
基本操作:int n(); //返回图中的顶点数int first(int); //返回该点的第一条邻边int next(int); //返回该点的下一条邻边void setEdge(int,int,int); //为有向边设置权值int getMark(int); //获得顶点的标志值void setMark(int); //为顶点设置标志值(2)队列ADT数据对象:D={ai | ai∈ElemSet, i=1,2,...,n, n≥0}数据关系:R1={ <a i-1,ai > | ai-1, ai ∈D, i=2,...,n}其中a1 端为队列头,an 端为队列尾基本操作:virtual void clear() = 0; //Reinitialize the queuevirtual bool enqueue(const Elem&) = 0; // insert an element at the back of thequeue.virtual bool dequeue(Elem&) = 0; // Remove the element from the front of thequeue.virtual bool frontValue(Elem&) const = 0; // Get a copy of the front element in thequeuevirtual int length() const = 0; // Return the number of elements in the queue.2.算法的基本思想:建立一个节点类,存储课程信息。
建立一个线性表类,将每次输入的课程信息存储到线性表内,完成线性表的构建。
建立一个图类,从线性表内读取顶点信息。
每次输入先修关系,则对节点创建对应指针,完成图的构建。
完成图的构建之后,使用拓扑排序得到有序课程表。
每当节点入度为0,输出该节点存储的课程信息,并删除该节点,同时与该节点相连的各个顶点的入度均减1。
重复以上操作直到图为空。
排序成功,输出排序后的顶点序列;否则,若入度为0的节点数小于节点总数,则不存在满足条件的课程表,输出提示信息,结束程序。
3.程序的流程输入模块:读入图的信息(顶点和边,用线性表进行存储)。
处理模块:topsort算法得出课程顺序。
输出模块:若排序成功,输出所排列的课表,否则,输出排序失败提示信息。
函数调用关系图:详细设计Graph G(n,m);G.pushVertex(); G.pushEdge(); G.topsort();三、1. 物理数据类型由于课程的编号为字符串,使用char 类型或string 类均可存储。
由于用户输入的课程个数不定且有序,所以用链式队列来存储排序后的顶点。
2. 伪代码(1) 创建类 //节点类 class Node {public: string node; int position; //位置 Node* next; bool visit; //用来标记节点是否被访问 Node() //初始化节点 { visit = false; next = NULL; position = 0; node = ' '; }void Graph::pushV ertex()主函数void Graph::pushEdge() void Graph::topsort()void line::insert(pos2,ch2)};//线性表类class Line{public:int num; //线性表中元素的个数Node* head;Node* rear;Node* fence;Line(){num = 0;head = fence = rear = new Node();}void insert(int v, string ch);//插入元素};//图类class Graph{private:int numVertex;//所输入的课程总数int numEdge; //先修关系的个数Line* line;public:Graph(int v, int e){numVertex = v;numEdge = e;line = new Line[v];}//读入点void pushVertex(); //读入顶点信息,表示课程信息void pushEdge(); // 读入有向边信息,有向边表示先修关系void topsort(); //拓扑排序;}(2)类的操作函数a.void Line: :insert(int v, string ch) //向线性表中插入元素{Node* current = new Node();current->node=ch;current->position=v;fence->next=current;fence = current;num++;}b.void pushVertex(){string ch;for(int i=0; i<numVertex; i++){cout<<"请输入点,即课程编号"<<i+1<<":";cin>>ch;line[i].head->node=ch;line[i].head->position=i;}}c.void Gragh:: pushEdge() // 读入有向边信息,有向边表示先修关系{string ch1, ch2;int pos1, pos2;for(int i=0; i<numEdge; i++){cout<<"请输入边,即先修关系"<<i+1<<":";cin>>ch1>>ch2;for(int j=0; j<numVertex;j++){if(line[j].head->node==ch1)pos1=j;if(line[j].head->node==ch2){pos2=line[j].head->position;break;}}line[pos1].insert(pos2,ch2);}}d.void Gragh::topsort() //拓扑排序{int i;int *d=new int[numVertex];for(i=0; i<numVertex; i++)d[i]=0; //数组初始化for(i=0; i<numVertex; i++){Node* p=line[i].head;while(p->next=NULL){d[p->next->position]++; //计算每个点的入度p=p->next;}}int top=-1, m=0, j, k;for(i=0; i<numVertex; i++) //寻找入度为0 的顶点,作为拓扑排序的起点{if(d[i]==0){d[i]=top; //找到第一个入度为0的点top=i;}while(top!=-1){j=top;top=d[top];cout<<line[j].head->node<<" ";m++;Node* p=line[j].head;while(p->next!=NULL) //每当访问一个顶点,输出该顶点存储的节点信息,同时删除从图中删除该顶点,直到图为空,结束排序。
{k=p->next->position;d[k]--; //当起点被删除时,后面的点入度-1if(d[k]==0){d[k]==top;top=k;}p=p->next;}}}cout<<endl;if(m<numVertex) //输出点的个数小于输入点的个数,不能完全遍历{cout<<"网络存在回路!"<<endl;delete[] d;}}};(3)主函数int _tmain(int argc, _TCHAR* argv[]){int n, m;cout<<"请输入节点的个数和边的个数:"<<endl;cin>>n>>m;Graph G(n,m);G.pushVertex();G.pushEdge();G.topsort();return 0;}3.算法的具体步骤a.输入信息:输入课程信息及先修关系;b.构建图:创建节点类存储课程信息。