《程序设计课程设计》指导书.docx
《《程序设计课程设计》指导书.docx》由会员分享,可在线阅读,更多相关《《程序设计课程设计》指导书.docx(52页珍藏版)》请在冰豆网上搜索。
《程序设计课程设计》指导书
程序设计课程设计
指导书
软件学院软件工程系
2013年6月15日
前言
《程序设计课程设计》是计算机科学与技术专业的重要实践性课程。
目的在于培养学生分析问题和解决问题的能力,为学生提供了一个既动手又动脑,独立实践的机会。
将课本上的数据结构、离散数学和C语言的理论知识和实际应用问题进行有机结合,提高学生程序设计、程序调试及项目开发能力。
为后续课程:
操作系统、软件工程,编译原理等课程的学习奠定必要的实践基础。
本课程设计是利用数据结构、离散数学、C语言理论和实验课中学到的编程知识和编程技巧,通过布置具有一定难度、一定编程量的课程设计题目,利用C语言作为开发工具,使学生通过课程设计掌握高级编程语言的知识和编程技术,掌握程序设计的思想和方法,初步具备利用计算机求解实际问题的能力。
通过《程序设计课程设计》课程的学习,能够帮助学生加深理解数据结构、离散数学、C语言基本概念,达到培养学生良好程序设计的习惯和运用C语言编写程序解决实际问题的能力。
使学生学会把书本知识用于解决实际问题,起到深化理解和灵活掌握教学内容的目的。
同时使学生在程序设计方法及上机操作等基本技能和科学作风方面受到比较系统和严格的训练。
通过该课程设计,学生应该掌握C或C++语言程序设计的方法、数据结构和离散数学理论知识,熟悉C或C++程序的开发环境及C或C++程序的调试过程,巩固和加深对理论课中知识的理解,提高学生对所学知识的综合运用能力;学生应该具有如下基本技能:
培养学生查阅参考资料、手册的自学能力,通过独立思考深入钻研问题,学会自己分析、解决问题。
通过对所选题目方案分析比较,确立方案,编制程序与调试程序。
能熟练调试程序,在教师的指导下,完成课题任务。
根据个人的设计调试过程,按课程设计报告的要求撰写设计报告。
选用教材及主要参考书:
1教材
呼克佑.C语言程序设计电子工业出版社,2012
严蔚敏.数据结构(C语言版)清华大学出版社,2012
2、主要参考书
[1]谭浩强.程序设计题解与上机指导(三版).清华大学出版社,2005
[2]邱仲潘.C语言参考手册.机械工业出版社,2004
[3]谭浩强.C语言程序设计(三版).清华大学出版社,2005
[4]方世昌.离散数学.西安电子科技大学出版社,2003
[5]丁亚涛.C语言程序设计.高等教育出版社,2003
一.课程设计报告要求
课程设计报告封面应给出专业、班级、姓名、学号、指导教师和完成日期,报告开头给出题目,内容包括以下五项:
1.【问题描述】
简要描述问题,然后说明程序设计的任务,程序要做什么。
明确规定以下内容:
(1)输入的形式和输入值的范围;
(2)输出的形式;
(3)程序所能达到的功能;
(4)测试数据:
包括正确的输入及其输出结果和含有错误的输入及其输出结果。
2.【设计需求及分析】
说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。
实现设计中定义的所有数据类型,对每个操作写出伪码算法;对主程序和其他模块也写出伪码算法(伪码算法的详细程度为按照伪码算法可以在计算机键盘直接输入高级程序设计语言程序);画出函数的调用关系图。
3.【设计功能的实现】(用C或C++描述)
//说明:
用C或C++实现代码设计。
4.【实例测试及运行结果】
列出测试结果,包括输入和输出。
测试数据应该完整、严格。
测试分析内容包括:
(1)测试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论与分析;
(2)算法的时空分析和改进设想;
(3)经验和体会。
5.【实现提示】
使用说明:
说明如何使用该程序,列出每一步的操作步骤。
附录:
列出程序文件名的清单以及必要的带注释的源程序。
心得体会等等。
二.课程设计报告示例——迷宫问题(参考)
专业:
班级:
姓名:
学号:
完成日期:
【问题描述】
编制一个求解迷宫通路的程序。
以一个m*n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。
设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。
求得的通路以三元组(i,j,d)的形式输出,其中:
(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。
如:
对于下列数据的迷宫,输出的一条通路为:
(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2)……
【设计需求及分析】
(1)以二维数组MAZE[M+2][N+2]表示迷宫,其中:
MAZE[0][J]和MAZE[M+1][J](0≤J≤N+1)及MAZE[I][0]和MAZE[I][N+1](0≤I≤M+1)为添加的一圈障碍。
数组中以元素值为0表示通路,1表示障碍。
限定迷宫的大小M,N≤10。
(2)用户以文件的形式输入迷宫的数据:
文件中第一行的数据为迷宫的行数M和列数N;从第2行至第M+1行(每行N个数)为迷宫值,同一行中的两个数字之间用空白字符相隔。
(3)迷宫的入口位置和出口位置可由用户随时设定。
(4)若设定的迷宫存在通路,则以长方阵形式将迷宫及其通路输出到标准输出文件(即
终端)上,其中,字符“#”表示障碍,字符“*”表示路径上的位置,字符“@”表示“死胡同”,即曾经经过但不能到达出口的位置,其余用空格符表示。
若设定的迷宫不存在通路,则报告相应信息。
(5)本程序只求出一条成功的通路。
然而,只需要对迷宫求解的函数作小量修改,便可求得全部路径。
【设计功能的实现】(用C或C++语言描述)
说明:
此内容由学生自己设计完成。
提示:
程序应包含的执行命令有:
1)创建迷宫;2)求解迷宫;3)输出迷宫的解。
概要设计示例如下:
1.设定栈的抽象数据类型定义为:
ADTstack{
数据对象:
D={ai|ai∈charset,i=1,2,……,n,n≥0}
数据关系:
R1={|ai-1,ai∈D,i=2……,n}
基本操作:
InitStack(&S)
操作结果:
构造一个空栈S。
DestroyStack(&S)
初始条件:
栈S已存在。
操作结果:
销毁栈S。
ClearStack(&S)
初始条件:
栈S已存在。
操作结果:
将S清为空栈。
StackLength(&S)
初始条件:
栈S已存在。
操作结果:
返回栈S的长度。
StackEmpty(&S)
初始条件:
栈S已存在。
操作结果:
若S为空栈,则返回TRUE,否则返回FALSE。
GetTop(S,&e)
初始条件:
栈S已存在。
操作结果:
若栈S不空,则以e返回栈顶元素。
Push(&S,e)
初始条件:
栈S已存在。
操作结果:
在栈S的栈顶插入新的栈顶元素e。
Pop(&S,&e)
初始条件:
栈S已存在。
操作结果:
删除S的栈顶元素,并以e返回其值。
StackTraverse(S,visit())
初始条件:
栈S已存在。
操作结果:
从栈底到栈顶依次对S中的每个元素调用函数visit().
}ADTstack
2.设定迷宫的抽象数据类型为:
ADTmaze{
数据对象:
D={ai,j|ai,j∈{‘’、‘#’、‘@’、‘*’},0≤i≤m+1,0≤j≤n+1,m,n≤10}
数据关系:
R={ROW,COL}
ROW={|ai-1,j,ai,j∈D,i=1,……,m+1,j=0,……,n+1}
COL={|ai,j-1,ai,j∈D,i=0,……,m+1,j=1,……,n+1}
基本操作:
InitMaze(&M,a,row,col)
初始条件:
二维数组a[row+2][col+2]已存在,其中自第1行至第row+1行、每行中自第1列至第col+1列的元素已有值,并且以值0表示通路,以值1表示障碍。
操作结果:
构成迷宫的字符型数组,以空白字符表示通路,以字符‘#’表示障碍,并在迷宫四周加上一圈障碍。
MazePath(&M)
初始条件:
迷宫M已被赋值。
操作结果:
若迷宫M中存在一条通路,则按如下规定改变迷宫M的状态:
以字符“*”表示路径上的位置,字符“@”表示“死胡同”;否则迷宫的状态不变。
PrintMaze(M)
初始条件:
迷宫M已存在。
操作结果:
以字符形式输出迷宫。
}ADTmaze;
3.本程序包含三个模块
1)主程序模块
voidmain()
{初始化
do{
接受命令;
处理命令;
}while(命令!
=“退出”);
}
2)栈模块----实现栈抽象数据类型
3)迷宫模块----实现迷宫抽象数据类型
4.求解迷宫中一条通路的伪码算法:
设定当前位置的初值为入口位置;
do{
若当前位置可通,
则{将当前位置插入栈顶;//纳入路径
若该位置是出口位置,则结束;//求得路径存放在栈中
否则切换当前位置的东邻方块为新的当前位置;
}
否则{
若栈不空且栈位置尚有其他方向未被探索,
则设定新的当前位置为沿顺时针方向旋转找到的栈顶位置的下一相邻块;
若栈不空但栈顶位置的四周均不可通,
则{删去栈顶位置;//后退一步,从路径中删去该通道块,
若栈不空,则重新测试新的栈顶位置,
直到找到一个可通的相邻块或出栈至栈空;
}
}
}while(栈不空);
{栈空说明没有路径存在}
详细设计示例如下:
1.坐标位置类型
typedefstruct{
intr,c;//迷宫中行、列的范围
}PosType;
2.迷宫类型
typedefstruct{
intm,n;
chararr[RANGE][RANGE];//各位置取值‘’,‘#’,‘@’或‘*’
}MazeType;
voidInitMaze(MazeType&maze,inta[][],introw,intcol)
//按照用户输入的row行和col列的二维数组(元素值为0或1)
//设置迷宫的初值,包括加上边缘一圈的值
boolMazePath(MazeType&maze,PosTypestart,PosTypeend)
//求解迷宫maze中,从入口start到出口end的一条路径
//若存在,则返回TRUE;否则返回FALSE
voidPrintMaze(MazeTypemaze)
//将迷宫以字符型方阵的形式输出到标准输出文件上
3.栈类型
typedefstruct{
intstep;//当前位置在路径上的“序号”
PosTypeseat;//当前的坐标位置
directiveTypedi;//往下一坐标位置的方向
}ElemType;//栈的元素类型
typedefstructNodeType{
ElemTypedata;
NodeType*next;
}NodeType,*LinkType;//结点类型,指针类型
typedefstruct{
LinkTypetop;
intsize;
}Stack;//栈类型
栈的基本操作设置如下:
voidInitStack(Stack&S)
//初始化,设S为空栈(S.top=NULL)
voidDestroyStack(stack&S)
//销毁栈S,并释放所占空间
voidClearStack(Stack&S)
//将S清为空栈
intstackLength(StackS)
//返回栈S的长度S.size
StatusStackEmpty(StackS)
//若S为空栈(S.top==NULL),则返回TRUE;否则返回FALSE
StatusGetTop(Stacks,ElemTypee)
//若栈S不空,则以e带回栈顶元素并返回TRUE,否则返回FALSE;
StatusPush(Stack&S,ElemTypee)
//若分配空间成功,则在S的栈顶插入新的栈顶元素e,并返回TRUE,
//否则栈不变,并返回FALSE
StatusPop(Stack&S,ElemType&e)
//若栈不空,则删除S的栈顶元素并以e带回其值,且返回TRUE
//否则返回FALSE
voidStackTraverse(Stacks,Status(*visit)(ElemTypee))
//从栈底到栈顶依次对S中的每个结点调用函数visit
其中部分操作的算法:
StatusPush(Stack&S,ElemTypee)
{//若分配空间成功,则在S的栈顶插入新的栈顶元素e,并返回TRUE;
//否则栈不变,并返回FALSE
if(MakeNode(p,e)){
p->next=s.top;s.top=p;
s.size++;returnTRUE;
}
elsereturnFALSE;
}
StatusPop(Stack&S,ElemType&e)
{//若栈不空,则删除S的栈顶元素并以e带回其值,且返回TRUE,
//否则返回FALSE,且e无意义
if(StackEmpty(S))returnFALSE;
else{
p=S.top;S.top=S.top->next;
e=p->date;S.size--;returnTRUE;
}
}
4.求迷宫路径的伪码算法:
StatusMazePath(MazeTypemaze,PosTypestart,PosTypeend)
{
//若迷宫中存在从入口start到出口end的通道,则求得一条存入在栈中
//(从栈底到栈顶为从入口到出口的路径),并返回TRUE;否则返回FALSE
InitStack(S);curpos=start;//设定“当前位置”为“入口位置”
curstep=1;found=FALSE;//探索第一步
do{
if(Pass(maze,curpos)){
//当前位置可以通过,即是未曾走到过的通道块留下足迹
FootPrint(maze,curpos);
e=(curstep,curpos,1);
Push(S,e);//加入路径
if(Same(curpos,end))found=TRUE;//到达终点(出口)
else{
curpos=NextPos(curpos,1);//下一位置是当前位置的东邻
curstep++;//探索下一步
}//else
}//if
else//当前位置不能通过
if(!
StackEmpty(S)){
Pop(S,e);
while(e.di==4&&!
StackEmpty(S)){
MarkPrint(maze,e,seat);Pop(S,e);
curstep--;//留下不能通过的标记,并退回一步
}//while
if(e.di<4){
e.di++;Push(S.e);//换下一个方向探索
curpos=NextPos(e.seat,e.di);//设定当前位置是该新方向上的相邻块
}//if
}//if
}while(!
StackEmpty(S)&&!
found);
returnfound;
}//MazePath
5.主函数和其他函数的伪码算法
voidmain()
{//主程序
Initialization();//初始化
do{
ReadCommand(cmd);//读入一个操作命令符
Interpret(cmd);//解释执行操作命令符
}while(cmd!
=‘q’&&cmd!
=‘Q’);
}//main
voidInitialization()
{//系统初始化
clrscr();//清屏
在屏幕上方显示操作命令清单:
CreatMaze—cMazePath—mPrintMaze—pQuit—q;
在屏幕下方显示操作命令提示框:
}//Initialization
voidReadCommand(char&cmd)
{//读入操作命令符
显示键入操作命令符的提示信息;
do{
cmd=getche()
}while(cmd[‘c’,‘C’,‘m’,‘M’,‘p’,‘P’,‘q’,‘Q’]);
}//ReadCommand
voidInterpret(charcmd)
{//解释执行操作命令
switch(cmd){
case‘c’,’C’:
提示用户输入“迷宫数据的文件名filename”;
从文件读入数据分别存储在rnum,cnum和二维数组a2中;
InitMaze(ma,a2,rnum,cnum);//创建迷宫
输出迷宫建立完毕的信息
break;
case‘m’,‘M’:
提示用户输入迷宫的入口from和出口term的坐标位置;if(MazePath(ma,from,term))//存在路径
提示用户察看迷宫;
else输出该迷宫没有从给定的入口到出口的路径的信息;
break;
case‘p’,‘P’:
PrintMaze(ma):
//将标记路径信息的迷宫输出到终端
}//switch
}//InterPret
6.函数的调用关系图反映了演示程序的层次结构:
主程序
InitializationReadCommandInterPret
InitMazeMazePathPrintMaze
InitStackPushPopStackEmptyStackTraverse
FootPrintMarkPrintPassNextPosSame
附录:
源程序文件名清单:
base.H//公用的常量和类型
stkpas.H//栈类型
maze.H//迷宫类型
testmaze.C//主程序
【实例测试及运行结果】
迷宫的测试数据如下:
左上角(1,1)为入口,右下角(9,8)为出口。
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
0
0
1
1
0
1
0
1
1
1
0
0
1
0
0
0
0
1
0
0
0
0
0
1
0
0
0
1
0
1
0
1
1
1
1
0
0
1
1
1
0
0
0
1
0
1
1
1
0
0
0
0
0
0
提示:
当入口位置为(1,1),出口位置为(9,8)时,输出数据应为:
*
*
#
@
@
@
#
*
#
@
@
@
#
*
*
@
@
#
#
#
*
#
#
#
#
@
*
*
*
#
*
*
*
@
#
*
*
*
#
*
#
#
#
#
#
*
#
#
#
#
*
#
#
#
*
*
测试结果示例:
三组测试数据和输出结果分别如下:
1.输入文件名为:
m1.dat,其中迷宫数据为:
32
00
00
00
入口位置:
11
出口位置:
32
求解路径后输出的迷宫:
*
*
*
*
2.输入文件名:
m2.dat,其中迷宫数据为:
34
0000
0011
0000
入口位置:
11
出口位置:
34
求解路径后输出的迷宫:
*
*
@
@
*
#
#
*
*
*
3.输入文件名:
m3.dat,其中迷宫数据同题目中的测试数据。
入口位置:
11
出口位置:
98
求解路径后输出的迷宫正确,并和需求分析中所列相同。
4.输入文件名:
m4.dat,其中迷宫数据为:
49
000000100
010001000
001110011
001110100
入口位置:
11
出口位置:
49
输出信息为:
此迷宫从入口到出口没有路径。
【实现提示】
计算机解迷宫通常用的是“穷举求解”方法,即从入口出发,顺着某一个方向进行探索,若能走通,则继续往前走;否则沿着原路退回,换一个方向继续探索,直至出口位置,求得一条通路。
假如所有可能的通路都探索到而未能到达出口,则所设定的迷宫没有通路。
可以二维数组存储迷宫数据,通常设定入口点的下标为(1,1),出口点的下标为(m,n)。
为处理方便起见,可在迷宫的四周加一圈障碍。
对于迷宫中任一位置,均可约定有东、南、西、北四个方向可通。
用户手册:
(1)本程序的运行环境为DOS操作系统,执行文件为:
TestMaze.exe
(2)进入演示程序后,即显示文本方式的用户界面:
(3)进入“产生迷宫(CreatMaze)”的命令后,即提示键入迷宫数据的文件名,结束符为“回车符”,该命令执行之后输出“迷宫已建成”。
(4)进入“求迷宫路径(MazePath)”的命令后,即提示键入入口位置(行号和列号,中间用空格分开,结束符为“回车符”)和出口位置(行号和列号,中间用空格分开,结束符为“回车符”),该命令执行之后输出相应信息。
若迷宫中存在路径,则执行此命令后,迷宫状态已改变,若要重复执行此命令,无论是否改变出口和入口的位置,均需重新输入迷宫数据。
(5)输入“显示迷宫”的命令后,随即输出当前的迷宫,即迷宫的初始状态或求出路径之后的状态。
心得体会:
1.本次作业比较简单,只有一个核心算法,即求迷宫的路径,所以总的调试比较顺利,只在调试MazePath算法时,遇到两个问题:
其一是,起初输出的迷宫中没有加上‘@’的记号,后发现是因为在MarkPrint函数中的迷宫参数丢失“变参”的原因;其二是,由于回退时没有将curpos随之减一,致使栈中路径上的序号有错。
2.栈的元素中的step域没有太多用处,可以省略。
3.StackTraverse在调试过程中很有用,它可以插入在MazePath算法中多处,以察看解迷宫过程中走的路径是否正确,但对最后的执行版本没有用。
4.本题中三个主要算法:
InitMaze,MazePath和PrintMaze的时间复杂度均为0(m*n),本题的空间复杂度亦为0(m*n)(栈所占最大空间)
5.经验体会:
借助DEBUG调试器和数据观察窗口,可以加快找到程序中疵点。
【选作内容】
(1)编写递归形式的算法,求得迷宫中所有可能的通路;
(2)以方阵形式输出迷宫及其通路。
三.设计题目(5选3)
1.保龄球计分
1.1【问题描述】
打保龄球是用一个滚球去撞击10个站立的瓶,将瓶击倒。
一局分10轮,每轮可滚球1次或2次,以击到的瓶数为依据计分。
一局得分为10轮得分之和,而每轮的得分不仅与本轮的滚球情况有关,还可能与后一轮或后两轮的滚球情况有关,即:
某轮某次滚球击倒的瓶数不仅要计入本轮得分,还可能会计入前一轮或两轮得分。
计分