编号:( )字 号
《软件课程设计》报告
班 级:姓 名:学 号:指导老师:
1
软件课程设计任务书
专业年级: 电子信息科学与技术 学生姓名:
任务下达日期: 2010 年 月 日
课程设计日期: 200 年 月 日至 200年 月 日
课程设计题目: 面向过程 类别 面向过程 题目序号 2、求n与其反序之和 6、编写程序求解骑士巡游问题 成绩 面向对象 图形界面 数据结构 3、 用三种方法通过虚函 数求Fibonacci数列 7、关于磁盘文档的输入输出 1、通过MFCAPPWIZARD 创建一个计算器 3.、求A矩阵的转置矩阵B。 4 、设计一个统计选票的算法,输出每个候选的得票结果
2
软件课程设计指导教师评阅书
指导教师评语(①基础理论及基本技能的掌握;②独立解决实际问题的能力;
③研究内容的理论依据和技术方法;④取得的主要成果及创新点;⑤工作态度及工作量;⑥总体评价及建议成绩;⑦存在问题等):
成 绩:
指导教师签字: 年 月
3
日
目录
一、面向过程设计题2 ------ 求n与其反序之和(1-2.cpp)
1.1 需求分析…………………………………………………………………...….…6 1.2 概要设计………………………………………………………………………....6 1.3 详细设计与编码………………………………………………………...……...7 1.4 调试分析………………………………………………………………………....7 1.5 用户使用说明 ………………………………………………………………….7 1.6 设计心得………………………………………………………………………....7
二、面向过程设计题6 ----- 编写程序求解骑士巡游问题(1-6.cpp)
2.1 需求分析…………………………………………………………………………7 2.2 概要设计………………………………………………………………………....8 2.3 详细设计与编码……………………………………………………………….10 2.4 调试分析………………………………………………………………………...10 2.5 用户使用说明 …………………………………………………………………11 2.6 设计心得………………………………………………………………………...11
三、面向对象设计题3
------ 用三种方法通过虚函数求Fibonacci数列
(2-3.cpp)
3.1 需求分析………………………………………………………………………..11 3.2 概要设计………………………………………………………………………..11 3.3 详细设计与编码………………………………………………………………13 3.4 调试分析………………………………………………………………………..13 3.5 用户使用说明 ………………………………………………………………...13 3.6 设计心得………………………………………………………………………..13
四、面向对象设计题7--------- 关于磁盘文档的输入输出(2-7.cpp)
4.1 需求分析…………………………………………………………………………14 4.2 概要设计………………………………………………………………………....14 4.3 详细设计与编码………………………………………………………………..15 4.4 调试分析………………………………………………………………………....15 4.5 用户使用说明 ………………………………………………………………….15 4.6 设计心得………………………………………………………………………....15
五、图形界面 ---------计算器(计算器)
5.1 需求分析……………………………………………………………………… ..16 5.2 概要设计………………………………………………………………………...16 5.3 详细设计与编码……………………………………………………………….19
4
5.4 调试分析………………………………………………………………………...19 5.5 用户使用说明 …………………………………………………………………21 5.6 设计心得………………………………………………………………………...21
六、数据结构3 ----- 求A矩阵的转置矩阵B(4-3.cpp)
6.1 需求分析…………………………………………………………………………21 6.2 概要设计………………………………………………………………………....21 6.3 详细设计与编码………………………………………………………………..23 6.4 调试分析………………………………………………………………………....23 6.5 用户使用说明 ………………………………………………………………….23 6.6 设计心得………………………………………………………………………....23
七、数据结构4--------统计选票(4-4.cpp)
7.1需求分析………………………………………………………..…………..............24 7.2概要设计…………………………………………………………………................24 7.3详细设计与编码…………………………………………………………..............26 7.4调试分析…………………………………………………………………................26 7.5用户使用说明……………………………………………………………...............27 7.6设计心得………………………………………………………………....................27
八、课程设计总结-------------------------------------------------------28
5
一、面向过程设计题2 ------求n与其反序之和(1-2.cpp)
1.1需求分析
编程序,从键盘输入正整数n,求出n与其反序数之和并输出。例如,输入2038,输出应为 2038+8302=10340。这道题最主要是用什么简单有效的方法求出一个正整数的反序。
用户在程序运行过程中应输入整数,当输入的数大于0时,输出这个数与其反序的和,然后继续输入整数;当输入的数非正时,程序结束运行。
1.2概要设计 流程图如下:
Main 函数 输入一个整数m 输出m+n 求m的反序n 判断是否大于0 N Y
End 求出一个正整数的反序,代码如下:
k=n;
m=0; while(k){ m=m*10+k%10; k=k/10; }
当k为0是停止循环,此时的m就是n的反序。
6
1.3 详细设计与编码
#include void main() { long n,m,k; cin>>n; while(n>0){ k=n; m=0; while(k){ m=m*10+k%10; k=k/10; } cout< 1.4 调试分析 经调试验证此程序能满足题目设计的要求。 1.5 用户使用说明 用户在程序运行过程中应输入整数,当输入的数大于0时,输出这个数与其反序的和,然后继续输入整数;当输入的数非正时,程序结束运行。 1.6 设计心得 在设计程序时,首先要明白程序要完成的任务,再分析程序的整体流程,然后找到程序的关键点,也就是最难的地方,比如这道题的关键点在于求反序。下面才是核心部分程序的编写,写完后,要对程序进行验证分析,看看有没有可以优化、修改的地方。 7 二、面向过程设计题6 ----- 编写程序求解骑士巡游问题(1-6.cpp) 2.1需求分析 编写程序求解骑士巡游问题:在n行n列的棋盘上,假设一位骑士(按象棋中“马走日”的行走法)从初始坐标位置(x1,y1)出发,要遍访(巡游)棋盘中的每一个位置一次。请编一个程序,为骑士求解巡游“路线图”(或告诉骑士,从某位置出发时,无法遍访整个棋盘 — 问题无解)。 当n=5且初始坐标位置定为(1,1),如下是一种巡游“路线图”。程序执行后的输出结果为: (x1,y1)? => (1=>5, 1=>5) : 1 1 1 6 15 10 21 14 9 20 5 16 19 2 7 22 11 8 13 24 17 4 25 18 3 12 23 当输入n和初始坐标后,不存在满足条件的路径则输出:无循环路径。 当输入的n>10或初始坐标不满足条件时输出:输入有误。 2.2设计概要 (1)“棋盘”可用整形二维数组B表示。某个位置还没有走用表示,第几步走到就用几表示。 (2)综合分析此题用递归算法设计要简单些。编制一个具有如下原型的递归函数solve,用于完成任务:从(i,j)点出发,做第k至第n*n(即n的平方)次的移动 — 将k直到n的平方这些数码按规则分别摆放到棋盘即数组B中,若成功则将全局bool型变量flag赋值true。 void solve(int k, int x, int y,); (3)编制主函数,让用户输入作为巡游起点的初始坐标位置(x1,y1),而后进行调用“solve(1,x,y );”来完成所求任务。 主函数流程图: Main 函数 输入棋盘维数n和初始位置(x,y) 判断输入正确性 Y 调用solve函数 8 N 输出”输入有误“ false 判断flag的值 true 输出“无巡回路径” End Solve 函数流程图: Solve(k,x,y) 函数 Flag为 false&& (x,y)没被走 Y N a[x][y]=k 退回到上一丛solve Y k=n*n N Flag=true 9 a[x][y]=0 输出路径 Solve(k+1,g,h) N Y 还有没走过的 2.3 详细设计与编码 #include int a[max][max]={0}; int n; void solve(int k,int x,int y) { int i,j; if(!(x>=0&&x solve(k+1,x-2,y+1); solve(k+1,x-1,y-2); solve(k+1,x-2,y-1); a[x][y]=0; } void main() { int x,y,k=1; cin>>n>>x>>y; if(n<=0||n>=max||x<=0||x>n||y<=0||y>n){ cout<<\"输入有误!\\n\"; return; } cout<<\"(x1,y1)? => (1=>\"< 情况二: 11 情况三: 经调试,发现程序都能准确执行输出正确结果,但有一个问题,当n过大,n=8、9、10的时候,程序需执行相当长的时间后,才能输出结果。如果用栈的思想将递归改为非递归,也许会提高效率、 另外对程序调整一点后,可以计算出每种条件下,循环路径的个数。例如: 2.5 用户使用说明 用户在使用本程序时首先要输入棋盘的维数和骑士的初始位置,按下enter键,程序变会根据输入执行输出结果,在棋盘维数过大时,可能要稍等片刻。要注意,此程序输出的结果只是巡回路径的一种,要想得到所有路径,需要对程序稍加改动。 2.6 设计体会 编写此程序后,我对递归思想有了更深刻的理解,递归算法设计的时候比较简单,符合人的思维过程,但在执行中需要更大的时间与空间。 三、面向对象设计题3------用三种方法通过虚函数求Fibonacci数列(2-3.cpp) 3.1需求分析 12 利用虚函数手段,按照3种不同的计算方法来求出Fibonacci数列的第n项(的具体项值)并输出。具体地说,可通过在基类baseCla及其派生类fib1Cla、fib2Cla和fib3Cla中说明如下的同一个虚函数“virtual double fib(int n);”,来实现求Fibonacci数列第n项值并返回的3种不同求解方法。例如,简单变量“数据平移”法、使用数组的实现法以及使用递归函数的实现法。 Main 函数如下: void main() { fib1Cla obj1; //fib1Cla类对象obj1 fib2Cla obj2; //fib2Cla类对象obj2 fib3Cla obj3; //fib3Cla类对象obj3 cout<<\"------------ fib1Cla ------------\"< fib(1476)=1.306989223763399e+308 ------------ fib2Cla ------------ fib(888)=1.704274475850073e+185 ------------ fib3Cla ------------ fib(35)=9.227465000000000e+006 3.2设计概要 首先,定义一个基类basecla,有一个纯虚函数。再定义其三个派生类fib1Cla、fib2Cla和fib3Cla,分别说明如下的同一个虚函数“virtual double fib(int n);“,最后定义一个fun函数来输出结果。 方法一数据平移fib函数流程图; fib(n) i=3;old1=1;old2=2; i<=n N newitem=old1+old2;old1=old2; old2=newitem; Y 13 返回newitem End 方法二数组法fib函数流程图: a[i]=a[i-2]+a[i-1] fib(n) fib(n) i=3;a[1]=1;a[2]=2; i<=n Y N 输出a[n] End 方法三递归法fib函数流程图: N n=1||n=2 Y Return fib(n-1)+fib(n-2) 返回:1 3.3 详细设计与编码 #include 14 using namespace std; class baseCla { //自定义的基类baseCla public: virtual double fib(int n)=0; //基类baseCla中说明了一个虚函数fib,且为纯虚函数 }; class fib1Cla:public baseCla { //由基类baseCla派生出的fib1Cla类 public: virtual double fib(int n); //派生类中说明同一个虚函数fib(简单变量\"数据平移\"法) }; double fib1Cla::fib(int n) { int i; double old1,old2,newitem; old1=1;old2=1; for(i=3;i double fib2Cla::fib(int n) {double a[2000]; int i; a[0]=0; a[1]=a[2]=1; for(i=3;i<=n;++i) a[i]=a[i-1]+a[i-2]; return a[n]; } class fib3Cla:public baseCla { //派生类fib3Cla public: virtual double fib(int n); //派生类中说明同一个虚函数fib(使递 15 归求解法) }; double fib3Cla::fib(int n) { if(n==1||n==2) return 1; else return fib(n-1)+fib(n-2); } void fun(baseCla *p, int n) { double d = p->fib(n); //根据p指针值的不同,将调用不同派生类的虚函数fib cout.flags(ios::scientific); cout.precision(15); cout<<\"fib(\"< 运行后输出结果是: 16 发现数据平移法效率最高,递归法效率最低。 3.5 用户使用说明 本程序只能计算特定数字的Fibonacci数列,要想计算其他数字的,需要修改主函数 3.6 设计体会 在设计程序时,有时可以尝试使用不同的算法,比较它们的效率,以便选出最优的设计方法。 在写类的时候可以使用继承与虚函数。 四、面向对象设计题7 -------- 关于磁盘文档的输入输出(2-7.cpp) 4.1 需求分析 编程序CompFile,首先让用户输入两个文件名及其路径(二文件均为text文件),而后通过使用类成员函数getline逐行读入这两个指定文件的内容并进行比较。若发现有不同,则在屏幕上显示出相异二行的行号及其内容,并暂停下来询问用户是否需要继续比较后继行,直到用户回答不需要继续进行比较,或者已经比到了二文件的结束时停止处理。 4.2 设计概 函数流程图: Main 函数 失败 读取文本1、2 成功 j=1 失败 获取文本1、2第j行 成功 逐个比较文本1、2第j行每个字母 全部相同 17 存在 不同 输出j和j行相异的内容 j++ 是否继续 Y N End 此题的关键在于文本的读取,与如何获取每行的内容。 4.3 详细设计与编码 #include cout<<\"文本1:\"< 4.4 调试分析 运行结果如下: 经运行验证,程序能基本满足要求。 4.5 用户使用说明 用户在使用过程中根据自己需要输入“Y”或“N”即可。 4.6 设计体会 通过此题,我复习了文件流的输入与输出,现在能基本做到灵活运用。输入输出流作为c++程序设计的基础,必须要灵活掌握。 19 五、图形界面 --------- 计算器(计算器) 5.1 需求分析 设计一图形界面,用来计算数据的加减乘除。 5.2 设计概要: 计算器的图形界面设计主要运用到对话框的处理与映射,构建一个对话框,植入相应的控件,例如,静态文本框,编辑框,单选框,组框,按钮等,然后对每个控件消息进行消息映射,最后添加对话框代码。 控件布局如下: 第一个文本框用于显示用户输入的表达式,第二个文本框用于在按下“=”键后显示根据表达式计算出的结果,“清除”键用于清除输入的表达式,“删除”按键删除错误的输入,其余按键用于输入表达式。 本次程序设计的第一个关键点就是用户界面的合理排版与美观设计,在从用户界面获取表达式之后,又一个关键点出来了,就是如何根据表达式计算出结果。 为了计算出结果,我采用了栈的思想。在这里我定义了一个运算符栈(char 类型的)和数字栈(double 类型的)。 运算符栈定义如下: class StackChar { private: 20 char *base; char *top; int stacksize; public: StackChar(); void DestroyStack(); void ClearStack(); int StackLength(); int com(char m,char t); 比较运算符优先级 bool IsEmpty(); bool GetTop(char &e); 获取栈顶元素 bool Pop(char &e); 弹出栈顶元素 bool Push(char e); 放到栈顶 }; 数字栈的定义与运算符栈类似,就不再赘述。 21 “=”按键函数流程图如下: 开始 StackChar op; StackDouble re; 输入运算符或数字s 判断s是否为结束符 否 数字 是 判断s的类型 运算符 把s放到re栈顶 取op栈顶元素t s>t 比较s和t优先级 S 5.3 详细设计与编码 (见提交文档) 5.4 调试分析 23 24 经调试验证,本程序能正确输出计算结果,对错误的输入能提示。 5.5 用户使用说明: 首先,程序给出计算器界面,用户根据自己需要,输入计算表达式,输入错误可按删除键,输入完毕点击运算按钮,则程序会给出运算结果。计算下一题前应按下清除键,表达式输入错误时,程序会自动提示。 5.6 设计心得: 本次实验是第一次接触MFC图形界面设计,通过对话框和各种控件的运用使程序以更加符合人们视觉要求的方式展现在用户的面前,能够完成简单的加、减、乘、除运算并能根据输入数据判断数据输入是否正确。 本程序的稳定性和健壮性有待进一步提高。 当然,本程序也能够通过扩展运算较复杂的各类运算。 六、数据结构3 ----- 求A矩阵的转置矩阵B(4-3.cpp) 6.1 需求分析 25 编程序,按如下方法求A矩阵的转置矩阵B:输入两个正整数m和n,而后通过使用指针配合new运算符生成一个m行n列的二维动态数组A以及另一个n行m列的二维动态数组B,之后为A输入数据(A矩阵数据),进而求出其转置矩阵B(数据放动态数组B中)并输出结果。 首先,输入矩阵A的行数及列数,再输入矩阵A的每个元素,按下enter键,便会输出矩阵A和其转制矩阵B。注意矩阵的元素是整形。 6.2 设计概要 在这里我们定义了一个矩阵类,用与完成矩阵的各种操作: class matrix{ private: nt row; 列数 nt line; 行数 elemtype **data; 存储元素的动态空间 public: matrix(int l=0,int r=0); 构造函数 void creat(); 输入各元素的值 void output(); 输出矩阵 matrix zhuanzhi(); 求转置矩阵 }; 此题对我来说有一个关键就是如何申请一动态二维空间,通过上网搜索我找到了常用的两种方法: 1、 har (*p)[4] = new char[100][4]; [1][2] = 'a'; . elete [] p; 2、 har **p = new char*[100]; r(i=0;i<100;++i) i] = new char[4]; . For(i=0;i<100;++i) delete [] p[i]; delete [] p; 在这里我用了第二种方法。 主函数流程图如下: Main 函数 输入行数列数 26 6.3 详细设计与编码 构造矩阵A 输入矩阵A的每个元素 求A的转置B 输出矩阵A和B End #include class matrix{ private: public: }; matrix::matrix(int l,int r){ int row; int line; elemtype **data; matrix(int l=0,int r=0); void creat(); void output(); matrix zhuanzhi(); 27 } void matrix::creat(){ } void matrix::output(){ } matrix matrix::zhuanzhi(){ } void main() int i,j; if(r>0&&r<=maxsize&&l>0&&l<=maxsize){ row=r; line=l; data=new elemtype*[line]; for(i=0;i int i,j; for(i=0;i int i,j; for(i=0;i matrix t(row,line); for(i=0;i { } 6.4 测试分析 程序运行结果如下: int line,row; cout<<\"输入矩阵A的行数和列数:\"; cin>>line>>row; matrix A(line,row),B; cout<<\"输入矩阵A的每个元素:\\n\"; A.creat(); B=A.zhuanzhi(); cout<<\"矩阵A是\\n\"; A.output(); cout<<\"矩阵B是\\n\"; B.output(); 经调试验证此程序能满足题目设计的要求。 6.5 用户使用说明 用户在使用过程中要正确输入行数、列数和每个元素。 6.6 设计体会 在设计程序的过程中,可能遇到自己不知道的地方,这就需要我们寻找这方面的资料,比如这道题,如何申请二维动态空间就是一个难点,总会找到解决的办法。 29 对于本题的矩阵类,我们还可以再增加一些成员函数,使其功能更加强大。比如,求两个矩阵的和、差或积,又如求一个矩阵的秩、最简行列式,又如一个方阵的逆等等。这样的话,这个矩阵类就更加完美了。 七、数据结构4--------统计选票(4-4.cpp) 7.1 需求分析 设计一个统计选票的算法,输出每个候选的得票结果,采用单链表存放选票,候选人编号依次为1,2,3,„„,N,且每张选票选且只选一人,0表示弃权, 其它数字表示给相同编号的候选人投票。 7.2 设计概要 这里题目要求我们采用链表的方法记录每张选票,那么我们就可以直接把以前写好的链表类拿过来使用。 struct LNode{ int data; 存储选票内容 struct LNode *next; }; 链表类定义如下: class LinkList{ private: LNode *List; int Length; public: LinkList(); bool ClearList(); bool IsEmpty (); int GetLength (); bool GetNode(int position, LNode** node); bool InsertNode(int beforeWhich, int data); bool GetNodeData(int position, int &data); bool DeleteNode(int position); }; 为了增加程序的可读性,使主函数更加简洁,我写了三个函数分别用于投票、 30 计票和输出计票结果。 void toupiao(LinkList &xuanpiao,int n) void jipiao(LinkList &xuanpiao,int *a) void shuchu(int *a,int n) 其中前两个函数多次调用了链表类的成员函数。 主函数的流程图如下: Main 函数 输入候选人个数n toupiao(xuanpiao,n) jipiao(xuanpiao,a) shuchu(a, n) End 31 toupaio函数流程图如下: void toupiao(LinkList &xuanpiao,int n) 输入temp temp是否满足条件 Y N N 调用InsertNode(1,temp) 是否继续 End 7.3 详细设计与编码 #include struct LNode{ int data; struct LNode *next; }; 32 Y class LinkList{ private: LNode *List; int Length; public: LinkList(); bool ClearList(); bool IsEmpty (); int GetLength (); bool GetNode(int position, LNode** node); bool InsertNode(int beforeWhich, int data); bool GetNodeData(int position, int &data); bool DeleteNode(int position); }; LinkList::LinkList(){ List=new LNode; List->next=NULL; Length=0; } bool LinkList::IsEmpty(){ if (List->next == NULL) return true; return false; } int LinkList::GetLength(){ return Length; } bool LinkList::ClearList(){ if (List == NULL) return false; LNode *p; while (List->next){ p = List->next; List->next = p->next; delete p; } Length = 0; return true; } 33 bool LinkList::GetNode(int position, LNode **node){ if (position<1||position>Length) return false; LNode *p=List; int i; for(i=0;i bool LinkList::InsertNode(int beforeWhich, int data){ LNode *p; if(beforeWhich==1) p=List; else if(!(GetNode(beforeWhich-1, &p))) return false; LNode *newNode = new LNode; newNode->data = data; newNode->next = p->next; p->next = newNode; Length++; return true; } bool LinkList::GetNodeData(int position, int &data){ LNode *p; if (!(GetNode(position, &p))) return false; data = p->data; return true; } bool LinkList::DeleteNode(int position){ LNode *p,*q; if(position==1) p=List; else if(!(GetNode(position-1, &p))) return false; q=p->next; p->next = q->next; delete q; 34 Length--; return true; } //LinkList的有元函数,用于投票,建立链表,输入的数据不满足要求时停止投票。0为弃权。 void toupiao(LinkList &xuanpiao,int n){ int temp; char q; cout<<\"输入的数据不满足要求时停止投票,输入0表示弃权。\\n\"; while(true){ cin>>temp; if(temp>=0&&temp<=n) xuanpiao.InsertNode(1,temp); else{ cout<<\"输入数据有误,是否继续?“Y”或“N”:\"; cin>>q; if(q=='N') return; } } } void jipiao(LinkList &xuanpiao,int *a){ int i=0,e; while(xuanpiao.GetNodeData(++i,e)) a[e]++; }