数据结构课程设计之课程表实现.docx
《数据结构课程设计之课程表实现.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计之课程表实现.docx(32页珍藏版)》请在冰豆网上搜索。
![数据结构课程设计之课程表实现.docx](https://file1.bdocx.com/fileroot1/2023-2/4/e4987a4c-7293-45a9-a0db-46ebf593d473/e4987a4c-7293-45a9-a0db-46ebf593d4731.gif)
数据结构课程设计之课程表实现
软件学院
课程设计报告书
课程名称数据结构
设计题目教学计划编制
专业班级软件10-04班
学号**********
姓名张小龙
指导教师刘玲玲
2012年1月
1设计时间
2012年1月3日至2012年1月5日
2设计目的
(1)加强学生分析问题能力和应用所学知识解决问题的能力;
(2)使学生对所学内容更深入的了解和应用;
(3)提高C程序调试能力,加强程序设计和实践能力;
(4)加深学生对《数据结构》和《C语言》等相关课程的认识;
(5)培养学生自主软件设计能力和开发能力;
(6)加强个人程序设计能力和学生与学生之间的交流和研讨。
3设计任务
大学的每个专业都要制定教学计划。
假设任何专业都有固定的学习年限,每学年含两学期,每学期的时间长度和学分上限值均相等。
每个专业开设的课程都是确定的,而且课程在开设时间的安排必须满足先修关系。
每门课程有哪些先修课程是确定的,可以有任意多门,也可以没有。
每门课恰好占一个学期。
试在这样的前提下设计一个教学计划编制程序。
任选软件专业几门课程作为顶点,通过这几门课程的先修关系来构建一个有向图,用邻接表来储存,通过栈和有向图来完成课程教学计划安排。
4设计内容
4.1需求分析
1、程序所能达到的功能
(1)数据结构使用有向图和栈。
(2)课程先修关系(表4.1--01)
(表4.1--01课程先修关系)
(3)如果输入的先修课程号不在该专业开设的课程序列内,则作为错误处理。
2、输入的形式和输入值的范围
输入参数包括:
学期总数,一学期的学分上限,每门课的课程号(固定占3位的字母数字串)、学分和直接先修课的课程号。
3、输出的形式
每学期课程安排
4、测试数据:
学期总数6,一学期的学分上限16,该专业共开课程数目14,按照表4.1--01输入课程名,课程号,课程学分。
输出正确的课程编排结果。
4.2总体设计
1、说明本程序中用到的所有抽象数据类型的定义
ADTGraph{
数据对象V:
V是具有相同特性的数据元素的集合,称为顶点集.
数据关系R:
R={VR}
VR={(v,w)|v,w∈V,(v,w)表示v和w之间存在直接先修关系}
基本操作P:
voidCreatGraph(ALGraph*G)
操作结果:
创造图G
voidInitStack(SqSttack*S)
操作结果:
构造一个空栈S
voidStackEmpty(SqStack*S)
初始条件:
栈S已存在
操作结果:
若栈S为空栈,则返回TRUE,否则FALSE
voidPush(SqStack*S,inte)
初始条件:
栈S已存在
操作结果:
插入元素e为新的栈顶元素
voidPop(SqStack*S,int*e)
初始条件:
栈S已存在且非空
操作结果:
删除S的栈顶元素,并用e返回其值
voidFindInDegree(ALGraphG,intindegree[])
初始条件:
拓扑排序完成
操作结果:
构造关键路径的先修关系网
voidTopologicalSort_1(ALGraphG,intnumterm,intuplcredit)
初始条件:
图G已存在
操作结构:
进行拓扑排序,并完成关系网的构造,使课程尽可能集中在前几个学期
voidTopologicalSort_2(ALGraphG,intnumterm,intuplcredit)
初始条件:
图G已存在
操作结果:
进行拓扑排序,并完成关系网的构造,使课程尽量均匀分布
}ADTGraph
2、说明主程序的流程
\
3、说明各程序模块之间的层次(调用)关系(图4.2-3)
4.3详细设计
1、实现概要设计中定义的所有数据类型,对每个操作只需要写出伪码算法
1)采用邻接表存储结构,构造没有相关信息的图G,并储存键入的相关信息
voidCreatGraph(ALGraph*G)
{通过循环语句完成对键入的课程名称,课程号,学分的存储,并课程先修关系建立邻接表
for(i=1;i<=G->arcnum;i++)/*构造顶点向量*/
{
printf("\n请输入存在先修关系的两个课程的序号:
");
scanf(&n,&m);
while(课程号不在编入范围)
{
printf("输入的顶点序号不正确请重新输入:
");
scanf(&n,&m);
}
分配头结点的存储空间
if(p为空)
{
printf("分配失败");
}
建立邻接表
}
printf("建立的邻接表);
for(i=1;i<=G->vexnum;i++)
{
printf("%d:
->",G->vertices[i].classid);
for(p=G->vertices[i].firstarc;p!
=NULL;p=p->nextarc)
printf("%d->",p->adjvex);
printf("NULL");
printf("\n");
}
}
2)构造一个空栈S
voidInitStack(SqStack*S)
{赋予顺序栈足够的存储空间
if(!
S->base)
{printf(存储分配失败)
exit
(1);
}
top=base初始栈为空,存储空间为所分配的足够的存储空间
}
3)判断是否为空栈
intStackEmpty(SqStack*S)
{if(栈S为空栈)
returnOK;
else
returnERROR;
}
4)入栈操作
voidPush(SqStack*S,inte)
{插入元素e为新的栈顶元素
if(栈满)
{为栈重新分配存储空间
if(!
S->base)
{
printf(存储分配失败)
exit
(1);
}
top=base初始栈为空,存储空间为所分配的足够的存储空间
}
栈顶指针上移
}
5.取栈顶操作
intPop(SqStack*S,int*e)若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
{if(栈顶元素为空)exit
(1);
栈顶指针下移并将其值返回给*e
}
6)求图中各节点的入度
voidFindInDegree(ALGraphG,intindegree[])
{
for(i=1;i<=G.vexnum;i++)
indegree[i]=0;
for(i=1;i<=G.vexnum;i++)
{
while(G.vertices[i].firstarc)
{
indegree[G.vertices[i].firstarc->adjvex]++;
G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
}
}
}
7)有向图G采用邻接表存储结构
voidTopologicalSort_1(ALGraphG,intnumterm,intuplcredit)
{若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则返回ERROR。
intindegree[M];存放各节点的入度
intcount;课程编排数目计数器
intsumcredit;每个学期的课程学分累加器
FindInDegree(G,indegree);对各顶点求入度indegree[0..vernum-1]
for(i=1;i<=G.vexnum;i++)
G.vertices[i].indegree=indegree[i];
初始化栈
while(count!
=G.vexnum&&k<=numterm)
{
sumcredit=0;
for(无先修的课程入栈)
if((G.vertices[i].indegree==0)&&(G.vertices[i].state==NOTSTUDY))
{
Push(&S,i);
G.vertices[i].state=STUDY;避免入度为零节点重复入栈
}
if(栈非空且学分计数器小于学分上限)
{
k=k+1;
printf("第%d个学期学得课程有:
\n",k);
for(i=1;i<=G.vexnum;i++)入度为零的节点入栈,即无先修的课程入栈
if((G.vertices[i].indegree==0)&&(G.vertices[i].state==NOTSTUDY))
Push(&S,i);
while(栈非空&&学分总数小于学分上限)
{
Pop(&S,&j);
sumcredit=sumcredit+G.vertices[j].credit;
if(学分计数器小于等于学分上限)
{
printf("%s",G.vertices[j].name);
课程数目累加
对j号顶点每个邻接点的入度减一
}
将未输出的节点重新压入栈
}
}
}
if(被编排课程<编排课程总数)
printf(课程编排出错);
else
{
printf(课程编排成功);
}
}
8)有向图G采用邻接表存储结构若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则返回ERROR
voidTopologicalSort_2(ALGraphG,intnumterm,intuplcredit)
{
头结点指针P
调用栈S
FindInDegree(G,indegree);
for(i=1;i<=G.vexnum;i++)
G.vertices[i].indegree=indegree[i];
InitStack(&S);
课程编排计数器赋值为0
课程名计数器赋值
maxnum=G.vexnum/numterm+1;
sumnum=0;
while(count!
=G.vexnum&&k<=numterm)
{
for(i=1;i<=G.vexnum;i++)入度为零的节点入栈,即无先修的if((G.vertices[i].indegree==0)&&(G.vertices[i].state==NOTSTUDY))
{
Push(&S,i);
G.vertices[i].state=STUDY;
}
if(栈非空,学分计数器小于学分上限)
{
k=k+1;
printf("第%d个学期学得课程有:
",k);
sumcredit=0;
sumnum=0;
for(i=1;i<=G.vexnum;i++)入度为零的节点入栈,即无先修的课程入栈
if((G.vertices[i].indegree==0)&&(G.vertices[i].state==NOTSTUDY))
Push(&S,i);while(栈非空&&学分总数小于学分上限&&学期课程数目小于学期最大数目)
{出栈
积分器累加
sumnum=sumnum+1;
if((sumcredit<=uplcredit)&&(sumnum<=maxnum))
{
printf("%s",G.vertices[j].name);
编排计数器累加
for(对j号顶点每个邻接点的入度减一)
G.vertices[p->adjvex].indegree--;
}
elsePush(&S,j);
}
}
}
if(课程未全部编排,有剩余)
printf(课程编排出错)
else
{
printf(课程编排成功)
}
}
2、对主程序和其它主要函数写出伪码算法
intmain()
{
intnumterm;/*学期总数*/
intuplcredit;/*一个学期的学分上限*/
intselectway;
建立邻接表
scanf("%d",&numterm);输入学期总数
scanf("%d",&uplcredit);输入一个学期的学分上限
printf("选择编排策略:
1.课程尽可能集中到前几个学期;2.课程尽量均匀分布");
if(选择1)
TopologicalSort_1(G,numterm,uplcredit);
if(选择2)
TopologicalSort_2(G,numterm,uplcredit);
return0;
}
3、画出函数的调用关系图(图4.3--03)
4.4测试与分析
4.4.1测试
学期总数:
6一学期的学分上限:
16该专业共开课程数目:
14
1.键入学期总数,学分上限,安排课程总数
2.输入课程名,课程号及相应学分
3.输入课程先修关系总数
4.顺序输入先修关系
5.输出邻接表
6.选择编排策略1,输出编排结果
7.选择编排策略2,输出编排结果
8.错误运行:
当输入两个相同课程号的不同课程,当输入先修顺序有环时…
9.运行结果:
4.4.2分析
1、调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析
调试过程中出现了很多问题,例如定义了无关变量、程序逻辑性错误等。
在种种错误下,自己根据提示修改了一部分简单错误。
对于其他错误,常常误认为是正确的,而不得改正。
无奈下求助同学帮助,在几个同课题同学帮助下,纠正了大多数问题。
其他几个复杂问题上网查阅资料,询问老师才最终解决。
设计两个不同拓扑的程序是遇到很多大麻烦,平时实验不涉及该内容,书上仅仅提供了一个算法。
在上网查阅资料,求助同学下才完成了框架。
面对程序考略不足,漏缺重要条件和关键步骤,遇到到了很大麻烦。
究其原因还是基础不牢,缺乏实验,动手能力差。
2、算法的时间复杂度和空间复杂度的分析
本程序算法的时间复杂度为O(n),空间复杂度为O(2n)
4.5附录
源程序代码及必要注释
/*Note:
YourchoiceisCIDE*/
#include"stdio.h"
#include"string.h"
#include"malloc.h"
#include"stdlib.h"
/*********图的邻接表存储表示*********/
#defineMAX_VERTEX_NUM100//最大课程总数
#defineSTACK_INIT_SIZE100//存储空间的初时分配量
#defineSTACKINCREMENT10//存储空间的分配增量
#defineOK1
#defineERROR0
#defineM100
#defineNOTSTUDY-1
#defineSTUDY1
typedefstructArcNode
{
intadjvex;//该弧所指向的顶点的位置
structArcNode*nextarc;//指向下一条弧的指针
}ArcNode;
typedefstructVNode
{
charname[30];//课程名
intclassid;//课程号
intcredit;//课程的学分
intindegree;//该结点的入度
intstate;//该节点的状态
ArcNode*firstarc;//第一个表结点的地址,指向第一条依附该顶点的弧的指针
}VNode,AdjList[100];
typedefintElemType;
typedefstruct
{
AdjListvertices;
intvexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;
typedefstruct
{
ElemType*base;//在栈构造之前和销毁之后,base的值为NULL
ElemType*top;//栈顶指针
intstacksize;//当前已分配的存储空间,以元素为单位
}SqStack;//顺序栈
/*********图的构建*********/
voidCreatGraph(ALGraph*G)//采用邻接表存储结构,构造没有相关信息的图G,并储存键入的相关信息
{inti,m,n,f[100]={0};
ArcNode*p;
printf("请输入需要编排课程总数:
\n");
scanf("%d",&G->vexnum);
for(i=1;i<=G->vexnum;i++)//构造顶点向量
{printf("请输入课程名\n");
scanf("%s",&G->vertices[i].name);
A:
printf("请输入课程号\n");
scanf("%d",&G->vertices[i].classid);
if(f[G->vertices[i].classid]){printf("课程号重复,请重新输入\n");gotoA;}
elsef[G->vertices[i].classid]=1;
printf("请输入该课程的学分\n");
scanf("%d",&G->vertices[i].credit);
G->vertices[i].indegree=G->vertices[i].state=NOTSTUDY;
G->vertices[i].firstarc=NULL;}
printf("请输入课程先修关系总数:
");
scanf("%d",&G->arcnum);
printf("请顺序输入每个课程先修关系(先修课程在前并以逗号作为间隔):
\n");
for(i=1;i<=G->arcnum;i++)//构造顶点向量
{
printf("\n请输入存在先修关系的两个课程的序号:
");
scanf("%d,%d",&n,&m);
while(n<0||n>G->vexnum||m<0||m>G->vexnum)
{
printf("输入的顶点序号不正确请重新输入:
");
scanf("%d,%d",&n,&m);
}
p=(ArcNode*)malloc(sizeof(ArcNode));
if(p==NULL)
{
printf("memoryallocationfailed,goodbey");
exit
(1);
}
p->adjvex=m;
p->nextarc=G->vertices[n].firstarc;
G->vertices[n].firstarc=p;
}
printf("\n建立的邻接表为:
\n");//输出建立好的邻接表
for(i=1;i<=G->vexnum;i++)
{
printf("%d:
->",G->vertices[i].classid);
for(p=G->vertices[i].firstarc;p!
=NULL;p=p->nextarc)
printf("%d->",p->adjvex);
printf("NULL");
printf("\n");
}
}
/********顺序栈的基本操作***********/
voidInitStack(SqStack*S)
{//构造一个空栈S
S->base=(int*)malloc(STACK_INIT_SIZE*sizeof(int));
if(!
S->base)
{printf("ERROR");//存储分配失败
exit
(1);
}
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
}
intStackEmpty(SqStack*S)
//若栈S为空栈,则返回TRUE,否则返回FALSE
{
if(S->top==S->base)
returnOK;
else
returnERROR;
}
/********入栈*********/
voidPush(SqStack*S,inte)
//插入元素e为新的栈顶元素
{
if(S->top-S->base>=S->stacksize)
{
S->base=(int*)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(int));
if(!
S->base)
{
printf("ERROR");//存储分配失败
exit
(1);
}
S->top=S->base+S->stacksize;
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;
}
/*******出栈*******/
intPop(SqStack*S,int*e)
{//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
if(S->top==S->base)exit
(1);
*e=*--S->top;
return0;
}
/******寻找各节点入度******/
voidFindInDegree(ALGraphG,intindegree[])//求图中各节点的入度
{
inti;
for(i=1;i<=G.vexnum;i++)
indegree[i]=0;
for(i=1;i<=G.vexnum;i++)
{
while(G.vertices[i].firstarc)
{
indegree[G.vertices[i].firstarc->adjvex]++;
G.vertices[i].firstarc=G.vertices[i].firstarc->nextarc;
}
}
}
/*****课程尽量集中到前几学期的拓扑排序*****/
voidTopologicalSort_1(ALGraphG,intnumterm,intuplcredit)
{/*有向图G采用邻接表存储结构。
若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则返回ERROR。
*/
inti=0,j,k;
ArcNode*p;
SqStackS;
intindegree[M];//存放各节点的入度
int