中南大学
《数据结构》课程设计
题 目一 运动会记分系统
题 目二 猴子选大王
题 目三 航班订票系统
学生姓名 兰 清 指导教师 盛 羽
学 院 信息科学与工程学院
专业班级 计算机科学与技术0403班
学 号 0902040319
完成时间 2006年1月12日
目 录
第一章 课程设计目的.............................................. 3
第二章 课程设计内容和要求........................................ 3
第三章 课程设计分析................................................ 4
第四章 结束语......................................................28
第五章 参考文献.................................................28
.
.
.
第一章 课程设计目的
通过课程设计题目的练习,强化学生对所学知识的掌握及对问题分析和任务定义的理解,对每到题目作出了相应的逻辑分析和数据结构的选择,通过对任务的分析,为操作对象定义相应的数据结构,以过程化程序设计的思想方法为原则划分各个模块,定义数据的抽象数据类型。分模块对题目进行设计,强化学生对C语言的掌握和对数据结构的选择及掌握。通过程序的编译掌握对程序的调试方法及思想,并且让学生学会使用一些编程技巧。促使学生养成良好的编程习惯,
以及让学生对书本上的知识进行了实践。
第二章 课程内容设计和要求
(一)问题分析和任务定义
对问题的描述应避开具体的算法和涉及的数据结构,它是对要完成的任务作出明确的回答,强调的是做什么,而不是怎么做。
(二)详细的设计和编码
算法的具体描述和代码的书写。
(三)上机调试
源程序的输入和代码的调试。
要求:设计中要求综合运用所学知识,上机解决一些与实际应用结合紧密的、规模较大的问题,通过分析、设计、编码、调试等各环节的训练,深刻理解、牢固的掌握数据结构和算法设计技术,掌握分析、解决实际问题的能力。
第三章 课程设计分析
3.1 题目一
首先声明,我不想按要求上的格式来写这个报告,因为他的条条框框太限制人的思维了,请见谅。另外,我也不会用一大堆的代码来凑字数,或者用一两个截图来炫耀程序界面,代码本身并不重要,重要的是实现功能的思想,或者是程序本身的创新,而且我不得不承认了一个友好的界面对于一个产品的成功是至关重要的,但是其决定因素还是你的产品所提供的功能。从本质上来讲这是一篇编程的反思,而绝对不是一份好的报告,下面我主要介绍介绍我编程的一些感想和收获。
猴子选王
这是此次实习的选作题目,题目如下:
5、猴子选大王
任务:一堆猴子都有编号,编号是1,2,3 ...m ,这群猴子(m个)按照1-m的顺序围坐一圈,从第1开始数,每数到第N个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子为大王。
要求:
输入数据:输入m,n m,n 为整数,n<m
输出形式:中文提示按照m个猴子,数n 个数的方法,输出为大王的猴子是几号 ,建立一个函数来实现此功能
因为刚学mfc觉得写一个windows界面的程序一定挺酷的,所以先前都把心思放在界面上了,是后来才优化的。
拿到这个题目很自然的想法是用一个循环的链表来实现它,假设猴子的个数是n,m是要淘汰的编号,那么建立一个n长的链表,链表最后一个元素的next指针指向第一个元素,这样就形成一个循环链表,而链表的数据域储存的就是猴子的编号。好那就顺着这种自然的想法我们来实现他。
首先是实现循环链表这个数据结构下面用c++的方式来实现:
下面给出我的实现,只给出定义,和一些重要的方法的实现:
template <class T> class CirList;//使用的是类模版
template <class T> class Node//结点定义
{
friend class CirList<T>;//定义友元是为了使用其私有的数据
private:
Node <T> *next;
T data;
public:
Node(Node<T> *pnext = NULL ):next(pnext){}
~Node(const T &item,Node <T> *pnext = NULL):data(item),next(pnext){}
void setnext(Node<T> * p){next = p;}
void setdata(T d){data = d;}
T getdata(){return data;}
~Node(){};
};//class Node
template <class T> class CirList//循环链表
{
private:
Node<T> *head;//头结点
Node<T> *current;//当前结点
int len;
public:
CirList(){ head=NULL; current=head; len=0;}
//编译器报错:HEAP[Data.exe]: Invalid Address specified to RtlValidateHeap( 00360000, 00366198 )
virtual ~CirList()//定义了指针并且为指针分配了空间的的类,一定要记得释放空间,否则会造成内存泄露。
{
if(head == NULL) return;
current=head;
Node<T> *work;
while(current->next != head)
{ work=current->next;
delete current;
current = work;
}
delete current;
}
T GetData();//返回当前结点的值
int Lenght();
void ToHead();//把当前结点移动到头结点
void PushBack(T d);//压入链表的尾部
void PopBack();//把尾元素弹出
void DelCur();//删除当前结点,并把后面一个结点补充上来
void Next();//当前结点后移
};template <class T>
//这里的堆分配有点奇怪
//删除一个元素后,原来的分配会变化
//也许系统会自动把后面的元素往前移动
//微软就是喜欢自作聪明
//
//Node<T> *work1=new Node<T>(0,NULL);
//Node<T> *work2=new Node<T>(1,NULL);
//Node<T> *work3=new Node<T>(2,NULL);
//delete work2;
//刚刚作了一个实验,证明实际上情况不是如此
//原来搞了那么久错误是:if(current = head)因该是if(current == head)
//好冤枉啊,也错怪了我们的盖茨兄
//在程序中很容易放此类错误,不过《专家编程》有一个很好的建议//就是,变量和常量比较是把常量放在==左边,这样如果你错写为//=号的时候编译器就会报错(试图为一个常量负值在c/c++中是非//法的),可见,好的编程习惯对一个程序员是多么重要
template <class T>
void CirList<T>::DelCur()
{
Node<T> *work;
work = head;
while(work->next != current)
work = work->next;
work->next = current->next;
if(current == head)//出错的地方(写成了current = head)
head = current->next;//删除表头的时候表头后移.不然会报错
delete current;
current = work->next;
len--;
}
基于这个结构的猴子选王的算法如下:(具体的说明在代码中解释)
//算法的主要部分,iTotal是猴子总数m,iChoose是要被淘汰的猴子编号n,
//返回值是猴子王的编号
//时间复杂度就是m+(m-1)X(n-1)了,而空间复杂度就是存m个数据的cirList用的空间了
//具体来说就是m X ( 4 + 4 ) + 8个字节了,
//其中4+4是结点Node<int>中data + next的空间,8是cirList中head + current的空间
//不过我想有更好的办法,想想吧
//改进想法但n很大的时候要经历很多次无用的循环
//然他对链表的长度取余数可以减少很多次循坏即(iChoose-1)%cirList.Lenght()
//大约n*(n-1)/2次
int MonkeyDlg::ChooseKing(int iTotal,int iChoose)//改进算法
{
m_Progress.SetRange32(0,iTotal-1);m_Progress.SetPos(0);
m_MProgress.SetRange32(0,iTotal);
m_MProgress.SetPos(0);//进度条的初始化
int i;
CirList<int> cirList;
for(i=0;i<iTotal;i++)//循环次数m
{
cirList.PushBack(i+1);//构建一个循环链表
m_MProgress.StepIt();//这里是设置进度条的位置用于显示进度
}
cirList.ToHead();
for(i=0;i<iTotal-1;i++)//循环次数m-1
{
for(int j=0;j < (iChoose-1)%cirList.Lenght();j++)
//移动到淘汰的猴子处,循环次数n-1(改进部分)
//原来是for(int j=0;j<iChoose -1;j++)
//这样有许多次循环是无用的
{cirList.Next();}
cirList.DelCur();//淘汰这只没用的猴子
m_Progress.StepIt();//这里是设置进度条的位置用于显示进度
}return cirList.GetData();}
程序运行的界面如下,还过的去啊,也不枉我花了这么多心思在里面:
上面是第一种实现,但并不是最好的,测试的时候出现的问题是,运行起来很慢,而主要的是间是花在分配空间和回收空间上,由此得出结论,不论是c style 的malloc();free();或是c ++ style 的new
Delete都是很费时间的事,所以我们可以想办法减少空间分配和回收的次数来提高效率,一个很好的想法是一次分配足够的空间,在一次回收,另外,c++中会对类额外照顾(好像很不公平,但事实就是这样),这也是很费时间的,而对于这种小的程序来说c是最好的方式。这也许就是c和c++的最好的平衡点了:c++用来做框架设计
而一些对时间要求特别高的方法用c 或者是汇编来实现;对于猴子选王来说,我想c是最好的方式,下面我们就来看一下c的实现,运行并且比较一下,你就会发现其中的差别,使你所想象不到的。
C 的实现如下:
Typedef Struct node//节点结构体
{
Int data;
Struct node *next;
}node,*pnode;
Typedef struct //链表结构体
{
Pnode head;
Int length;
}cirlist,*plist;
//下面是实现的具体算法,为了节约空间,main函数就不给出了
Int chooseking(int itotal,int ichoose)
//itotal是猴子总数,ichoose是要淘汰的倒霉猴子的编号
{
If(ichoose == 1){Return itotal;}//ichoose=1的情况太简单了,就没必要复杂化了
int I,j;
Cirlist cl;
Pnode pwork;
cl.head = (pnode)malloc(itotal * sizeof(node));//一次分配够
cl.length = itotal;
for(i=0;i<cl.length;i++)//初始化形成一个链表
{cl.head[i].data=I;cl.head[i].next=&head[i+1];}
cl.head[cl.length -1] = cl.head;//使之成为一个循环链表
for(i=0;i<itotal-1;i++)//要淘汰itotal -1 只猴子,最后剩下一只就是大王了
{ //移动到要淘汰猴子的前一个位子所以是(ichoose-1)
Work = cl.head;
for(j=0;j<(ichoose-1)%cl.length;j++)
{work = work->next;}
//现在work是淘汰的前一个,下面的语句,就从链表中删除了那只该死的猴子(就是让其地址悬空)
Work = work->next->next;
cl.head = work->next;
cl.length --;
}
Itn result;
Result = cl.head->data;
Free(cl.head);
Return result;//返回猴王编号
}
简洁把,最重要的是效率很高,希望你也有同感。下面在以娱乐的方式给出另一种最暴力,也是最低效的实现,使用数组实现的,通过它你会发现c指针是多么的方便和快捷。不罗嗦,来看一下吧。
//方法很简单,就不加注释了
Int chooseking(int itotal,int ichoose)
{
Int I,j;
Int sum;
char *monkeys;
Monkeys = (char *) malloc(itotal*sizeof(char));
Memset(monkeys,1,itotal);
J=0;
For(i=0;i<itotal-1;i++)
{
Sum=0;
While(1)
{sum += monkeys[j];
If(ichoose == sum)break;
J++;}
Monkeys[j] = 0;
}
For(i=0;i<itotal;i++)
If(1 == monkeys[i])return I;
Return ERROR;//-1
}
不知道大家认真看了上面的程序没,其实是可以优化的,不过我很懒,这个任务就交给你了。哈,我突然有有了另一种算法了,不知效率怎样,先看一下吧,是用标准c++模版库的容器写的
#include<vector>
Using namespace std;
Int chooseking(int itotal,int ichoose)
{
Int I,j;
vector<int> monkeys(itotal);
//相当与建立一个数组,不过这是一个强大的数组
For(i=0;i<itotal;i++)monkeys[i]=I;
Vector<int>::Iterator m;
M=monkeys.begin();
For(i=0;i<itotal -1;i++)
{
For(j=0;j<choose-1;j++)
{M++;if(m==monkeys.end())m=monkeys.begin();}
Monkeys.erase(m);//淘汰它
}
Return *m;
}
上面的算法满简洁的,但是它的效率就无从考证了,因为他的内部实现被封装起来了。
至此,所有的关于猴子的话题就到此为止了,其实是还有好多可以说的,下次我们把孙悟空叫来一起讨论吧,这里就算了。(其实,我是怕写的太多了,没钱打印^_^)
订票系统
2、订票系统
任务:通过此系统可以实现如下功能:
录入:可以录入航班情况(数据可以存储在一个数据文件中,数据结构、具体数据自定)
查询:可以查询某个航线的情况(如,输入航班号,查询起降时间,起飞抵达城市,航班票价,票价折扣,确定航班是否满仓);可以输入起飞抵达城市,查询飞机航班情况;
订票:(订票情况可以存在一个数据文件中,结构自己设定)可以订票,如果该航班已经无票,可以提供相关可选择航班;
退票: 可退票,退票后修改相关数据文件;客户资料有姓名,证件号,订票数量及航班情况,订单要有编号。
修改航班信息:当航班信息改变可以修改航班数据文件
要求:
根据以上功能说明,设计航班信息,订票信息的存储结构,设计程序完成功能。
事先说明,这个是我要花大力气说的,因为我花了很多时间到上面。也作了很多优化,并且是第一次用c++来写比较大一点的项目,其中的点点滴滴是记忆犹新的。
这个程序经历了一个由死到活的过程,之前有一个失败的版本。首先想到的是用mfc来写,原因不用说你也应该会很清楚,就是界面容易做,并且不难看。不过后来写到 实在是写不下去了,这个原因你就不清楚了,我要做一个详细的检讨:
错误之一:糟糕的设计
作为一个系统,首先因该要有良好了框架,然后再往框架里添功能,这样写起来有条不紊,并且便于调试和维护,因为整个框架结构很清晰,出了问题可以很快地定位。而我,跟本没有作哪怕是一点点可怜的设计,拿到就写,好像自己是高手是的,以为可以一次搞定,
写到后来就变成成天看电影了,甚至连碰都不想去碰它。其实这也是必然的。
错误之二:花了太多的心思到界面上
再次说明,界面是很重要的,但是它又是一个工程中重要不分最不重要的。其实,在程序的初期,用控制台来获得输入输出,是很方便的,这样我们可以更专注于功能的的实现,和算法的效率。等到所有功能都搞定了,有兴趣的话再为他加上一个gui这样会事半功倍,相信我。
错误之三:一开始就考虑优化
这是我很容易放的一个错误,现在又人建议,程序的初期先不考虑算法的效率,尽量不去考虑错误处理,用最直接的方式来完成系统所要的功能。等到整个工程在主框架内能够正常运行了,在去考虑算法的效率和错误处理。我在前期的时候就被算法的效率和错误处理搞得头晕脑胀,现在觉得这种做法是有其可取之处的。不知道你怎么看。
错误之四:用了mfc
个人认为mfc的设计很糟糕(尽管我对mfc只知道其皮毛,是一个典型的菜鸟),如果你是micsoft的fan千万不要拿石头扔我。我的原因是:首先,它的类套欠的太多了,层层叠叠,让人丈二和尚----莫不着头脑。第二,它的结构太固定化,让我们都按它的模式去做,限制人的思维,而且,工程做大的会显得有点乱。第三,封装的太多,封装是一件好事,省去了了那些重复性的工作,提高了工作效率,但是我认为,封装的太多就走向了另一个极端。这就好像用绳子捆住我们一样,发挥空间就小了。强烈鄙视micsoft的这种把程序员当傻瓜的做法。
错误之五:就是有太多的错误还不及时放手,等到放手的时候已经烂费了太多的时间。
我放了太多的错误,这些对你来说也许是显而易见的,但我是经历了失败才看到的,即使有很多地方我的观点是很不正确的,但对我来说也是一种财富,请不要以鄙视的眼光来看待我的这些结论。
废话少说,进入正题吧,下面我就介绍一下我的最终版本的订票系统的具体实现。
我把整个系统分割成了plan(航班),user(用户),order(订单),system(系统)四个类。各个功能模块的实现是在每一个类里面的。其中plan,user,order类的机构和设计是一个模式的,接下来一起介绍,system是系统得核心类,待会会详细说明。
Plan,user,order类结合代码说明(用注释的方式,这里只给出定义不分,重要算法会给出实现部分)
Order类:
#ifndef ORDER_H
#define ORDER_H
#include <string>
class order
{
private:
std::string id;//订单号
std::string pid;//此订单对应的航班号
std::string uid;//此订单的用户
std::string date;//订票时间
std::string num;//此订单对应机票数量
public:
//可读属性
std::string get_id();
std::string get_pid();
std::string get_uid();
std::string get_date();
int get_num();
//可写属性
void set_id(std::string value);
void set_pid(std::string value);
void set_uid(std::string value);
void set_date(std::string value);
void set_num(std::string value);
public:
order();//构造函数
~order();//析构函数
//下面这三个方法是每个类都有的
std::string toxml();
//非常有用的方法,把对象转化为xml文本
void load(std::string &buf);
//从字符串中加载订单,这个字符串可以是从文件里读出的
std::string face();
//用于显示订单信息的方法
};
#endif
Plan类
#ifndef PLAN_H
#define PLAN_H
#include <string>
class plan
{
private:
std::string id;//航班号
std::string sta_adr;//出发点
std::string arv_adr;//抵达点
std::string sta_time;//出发时间
std::string arv_time;//抵达时间
std::string price;//价格
std::string zhekou;//折扣
std::string total;//票的总数
std::string selled;//售出的票数
public:
std::string get_id();
std::string get_sta_adr();
std::string get_arv_adr();
std::string get_sta_time();
std::string get_arv_time();
float get_price();
float get_zhekou();
int get_total();
int get_selled();
int get_remain();
void set_id(std::string);
void set_sta_adr(std::string);
void set_arv_adr(std::string);
void set_sta_time(std::string);
void set_arv_time(std::string);
void plan::set_price(std::string value);
void plan::set_zhekou(std::string value);
void plan::set_total(std::string value);
void plan::set_selled(std::string value);
public:
plan();
~plan();
std::string toxml();
void load(std::string &buf);
std::string face();
void change(std::string ,std::string );
};
#endif
User类
#ifndef USER_H
#define USER_H
#include <string>
class user
{
private:
std::string id;//id用于登陆系统(readonly)
std::string pwd;//密码(read and write)
std::string power;//权限(readonly)
std::string name;//用户真实姓名(read and write)
std::string identity;//证件号码(read and write)
public:
user();
~user();
private:
public://属性
//读属性
std::string get_id();
std::string get_pwd();
std::string get_power();
std::string get_name();
std::string get_identity();
//写属性
void set_power(std::string new_power);
void set_pwd(std::string new_pwd);
void set_name(std::string new_name);
void set_identity(std::string new_identity);
void set_id(std::string value);
public://方法
void powerup(user &u,std::string new_power);//提升用户权限
void load(std::string);
std::string toxml();
std::string face();
};
#endif
这几个类实在是长得太像了,好像是三胞胎,所以我们只说明一个就行了,注意到他们的load()和 toxml()方法没,我们整个工程能够实现对xml的支持,就靠这两个方法了(至于为什么要用xml的方式来读写数据,就不说了,xml的好是不用说我们都明白的),以order类的load() 和 toxml() 做个简要的介绍:
string order::toxml()
//实现把对象转化到xml的格式,其规则是显而易见的
{
string result;
result += "<order><id>" + id + "</id>";
result += "<pid>" + pid + "</pid>";
result += "<uid>" + uid + "</uid>";
result += "<date>" + date + "</date>";
result += "<num>" + num + "</num>";
return result;
}
void order::load(string &buf)
//从字符串中加载对象,toxml()的逆过程
{
size_t sta=0,end=0;
sta = buf.find("<id>",0);//
end = buf.find("</id>",0);
sta += strlen("<id>");
id = buf.substr(sta,end-sta);
sta = buf.find("<pid>",0);//
end = buf.find("</pid>",0);
sta += strlen("<pid>");
pid = buf.substr(sta,end-sta);
sta = buf.find("<uid>",0); //
end = buf.find("</uid>",0);
sta += strlen("<uid>");
uid = buf.substr(sta,end-sta); //
sta = buf.find("<date>",0);
end = buf.find("</date>",0);
sta += strlen("<date>");
date = buf.substr(sta,end-sta);//
sta = buf.find("<num>",0);
end = buf.find("</num>",0);
sta += strlen("<num>");
num = buf.substr(sta,end-sta);//
}
还有一个方法是不得不说的,face()显示一个对象,它提供了一种简便的方式
string order::face()
{
string result;
result += "order id:" + id + '/n';
result += "user id:" + uid + '/n';
result += "plan id:" + pid + '/n';
result += "date:" + date + '/n';
result += "tikets number:" + num + '/n';
return result;
}
比起前面三个类的简洁和漂亮,system类就显得稍微复杂和零乱,如果还有时间的话应该是要再整理一下的,不管这么样,我们来看一下它的定义把
#ifndef SYSTEM_H
#define SYSTEM_H
#include <string>
#include <vector>
#include "user.h"
#include "plan.h"
#include "order.h"
class CSystem
{
private:
std::vector<user> users;//所有的用户
std::vector<plan> plans;//所有的航班
std::vector<order> orders;//所有的订单
user *cur_user;//当前用户
private://系统的一些设置
char *user_file;
char *plan_file;
char *order_file;
private:
//从文件中加载信息
void load_users();
void load_plans();
void load_orders();
void clear();// 清屏
void welcome();//显示欢迎信息,和版本信息
void help();//帮助
void login();//登陆
void logout();//登出
void badcmd(std::string cmd);//无法解析的命令
void signin();//注册
void show(std::vector<std::string> &);//显示
void insert(std::vector<std::string> &);//插入
void alert(std::vector<std::string> &);//更新
void find(std::vector<std::string> &);//查找
private://核心算法
void show_plans();//显示航班信息
void show_users();//显示用户信息
void show_orders();//显示订单信息
void insert_plan(std::vector<std::string> &);//添加航班
void insert_user(std::vector<std::string> &);//添加用户
void insert_order(std::vector<std::string> &);//添加订单
void alert_plan(std::vector<std::string> &);//修改航班
void alert_user(std::vector<std::string> &);//修改用户
void alert_order(std::vector<std::string> &);//修改订单
void find_plan(std::string ); //查找航班
void find_user(std::string );//查找用户
void find_order(std::string );//查找订单
void buy_tikets(std::string );//买票
void return_tikets(std::string );//退票
public:
std::string get_cmd();//获取命令
void parse_cmd(std::string);//解析命令
public://
CSystem();
~CSystem();
void startup();//启动系统
void shutdown();//关闭系统
};
#endif
由于方法太多就不一一介绍了,我们在核心算法中选几个讨论吧,为了弄清楚整个工程,我觉得有必要费一点时间讲一下我的设计思路.主函数非常的简洁,看上去是这样
int main(int argc,char **argv)
{
CSystem sys ;
string cmd;
sys.startup();
while(1)
{
cmd = sys.get_cmd();
sys.parse_cmd(cmd);
}
sys.shutdown();
return 0;
}
首先启动系统,让后由系统获得名令,再由系统解析命令,调用适当的函数完成用户的请求,命令解析部分不是这里这重点就不介绍了,不过还是有必要把本系统支持的命令及格式列出来(全是小写字母):
Login
logout
Sign in
Goodbye|bye|byebye
show me|mysele
show all plan|user|order
Insert plan ([航班号],[起点],[终点],[起飞时间],[抵达时间],[价格],[折扣],[载客量])
Insert user ([用户id],[密码],[证件号码])
Insert order ([订单号],[用户id],[航班号], [票数])
Find plan|user|order where([属性]=[属性的值],...)
Alert plan|user|order where([属性]=[值],…) set([属性]=[值],…)
Buy tikets ([航班号],[买票张数])
Return tikets ([订单号],[退票张数])
这种命令有点盗版sql的嫌疑,但我保证绝对好学,也容易用。但是实现这些命令不使用它们要难很多,就来看几个命令的实现:
Show命令,以Show all plan为例子,其他雷同
void CSystem::show_plans()
{
size_t i;
if(plans.size() == 0)
{
cout<<"these is no plan now"<<endl;
return;
}
cout<<"所有的机票信息如下:"<<endl;
for(i =0;i<plans.size();i++)
{
cout<<plans[i].face().c_str()<<endl<<endl;
}
cout<<endl;
}
Insert 命令,以insert plan 为例
void CSystem::insert_plan(vector<string> &vecarg)
{
if(cur_user == NULL)
{
cout<<"请先登入"<<endl;
return;
}
if(cur_user->get_power() != "admin" && cur_user->get_power() != "typer")
{
cout<<"权限太低!"<<endl;
return;
}
if(vecarg.size() <3)
{
cout<<"参数太少!"<<endl;
return;
}
if(vecarg.size() > 3)
{
cout<<"参数太多!"<<endl;
return;
}
vector<string> vectemp;
size_t sta=0,end =0;
while(1)
{
sta = vecarg[2].find_first_not_of("(,",sta);
if(sta == string::npos)
break;
end = vecarg[2].find_first_of("(,)",sta);
if(end == string::npos)
{
end = vecarg[2].size();
}
vectemp.push_back(vecarg[2].substr(sta,end-sta));
sta=end+1;
}
if(vectemp.size() <8)
{
cout<<"参数太少!"<<endl;
return;
}
if(vectemp.size() >8)
{
cout<<"参数太多!"<<endl;
return;
}
bool have=false;
size_t i;
for(i=0;i<plans.size();i++)
{
if(plans[i].get_id() == vectemp[0])
{
cout<<"此航班号已经存在,请换一个航班号!"<<endl;
return;
}
}
plan new_plan;
new_plan.set_id(vectemp[0]);
new_plan.set_sta_adr(vectemp[1]);
new_plan.set_arv_adr(vectemp[2]);
new_plan.set_sta_time(vectemp[3]);
new_plan.set_arv_time(vectemp[4]);
new_plan.set_price(vectemp[5]);
new_plan.set_zhekou(vectemp[6]);
new_plan.set_total(vectemp[7]);
new_plan.set_selled("0");
plans.push_back(new_plan);
cout<<"航班添加成功!"<<endl;
}
Alert命令,以alert plan 为例
void CSystem::alert_plan(vector<string> &vecarg)
{
vector<string> temp;
size_t sta=0,end=0;
sta = vecarg[2].find_first_not_of("( =)",sta);
end = vecarg[2].find_first_of("( = )",sta);
temp.push_back(vecarg[2].substr(sta,end-sta));
sta = vecarg[2].find_first_not_of("(= )",end);
end = vecarg[2].find_first_of("(= )",sta);
temp.push_back(vecarg[2].substr(sta,end-sta));
sta = vecarg[2].find_first_not_of("( =)",end);
end = vecarg[2].find_first_of("(= )",sta);
if(end == string::npos)end = vecarg[2].size();
temp.push_back(vecarg[2].substr(sta,end-sta));
if(temp[0] != "where" || temp[1] != "id")
{
cout<<"bad use of command alert"<<endl;
return;
}
size_t u=-1,i;
for(i=0;i<plans.size();i++)
{
if(plans[i].get_id() == temp[2])
{
u = i;
break;
}
}
if(u == -1)
{
cout<<"plan id is not exsit."<<endl;
return;
}
temp.clear();
sta=0;end=0;
while(1)
{
sta = vecarg[3].find_first_not_of("( =),",end);
if(sta == string::npos)break;
end = vecarg[3].find_first_of("( =),",sta);
if(end == string::npos)end = vecarg[3].size();
temp.push_back(vecarg[3].substr(sta,end - sta));
}
if(temp[0] != "set")
{
cout<<"bad use of command."<<endl;
return;
}
for(i=1;i<temp.size();i+=2)
{
plans[u].change(temp[i],temp[i+1]);
}
cout<<"修改完毕!"<<endl;
}
Buy tikets命令
-----------------------------------------------------------------
void CSystem::buy_tikets(string str)
{
if(cur_user == NULL)
{
cout<<"请先登入!"<<endl;
return;
}
size_t sta=0,end = 0;
sta = str.find_first_not_of("( ,)",end);
end = str.find_first_of("( ,)",sta);
string pid = str.substr(sta,end-sta);
sta = str.find_first_not_of("( ,)",end);
end = str.find_first_of("( ,)",sta);
if(end == string::npos)end = str.size();
string num = str.substr(sta,end-sta);
//find plan by id
plan *p = NULL;
size_t i;
for(i=0;i<plans.size();i++)
{
if(plans[i].get_id() == pid)
{
p = &plans[i];
break;
}
}
if(p == NULL)
{
cout<<"plan not find."<<endl;
return;
}
else
{
if(p->get_selled() + atoi(num.c_str()) > p->get_total())
{
cout<<"sorry , these only "<<p->get_remain()<<" remain."<<endl;
return;
}
char cnum[10];
itoa(p->get_selled()+atoi(num.c_str()),cnum,10);
string snum;
snum += cnum;
p->set_selled(snum);
char tem[10];
itoa(orders.size()+1,tem,10);
string oid;
oid += tem;
order new_order;
new_order.set_id(oid);
new_order.set_pid(pid);
new_order.set_uid(cur_user->get_id());
new_order.set_date("can not get date");
new_order.set_num(num);
cout<<"buy tikets ok./nyour order id is "<<new_order.get_id()<<endl;
cout<<endl<<new_order.face()<<endl<<endl;
orders.push_back(new_order);
}
return;
}
Return tikets命令
-----------------------------------------------------------------
void CSystem::return_tikets(string str)
{
if(cur_user == NULL)
{
cout<<"请先登录!"<<endl;
}
size_t sta=0,end=0;
sta = str.find_first_not_of("( ,)",end);
end = str.find_first_of("( ,)",sta);
string oid = str.substr(sta,end-sta);
sta = str.find_first_not_of("( ,)",end);
end = str.find_first_of("( ,)",sta);
if(end == string::npos)end = str.size();
string num = str.substr(sta,end-sta);
order *o= NULL;
unsigned i;
for(i=0;i<orders.size();i++)
{
if(orders[i].get_id() == oid)
o = &orders[i];
}
if(o == NULL)
{
cout<<"can not find the order as your requst"<<endl;
return;
}
if(o->get_uid() != cur_user->get_id())
{
cout<<"你不能退别人的票!"<<endl;
}
int inum = atoi(num.c_str());
if(inum > o->get_num())
{
cout<<"the number of tikets is to big"<<endl;
return;
}
char stemp[10];
itoa(o->get_num() - inum,stemp,10);
string strnum(stemp);
o->set_num(strnum);
plan *p = NULL;
for(i=0;i<plans.size();i++)
{
if(plans[i].get_id() == o->get_pid())
p = &plans[i];
}
if(p == NULL)
{
cout<<"plan not find while return order"<<endl;
return;
}
itoa(p->get_selled() - inum,stemp,10);
string sselled(stemp);
p->set_selled(sselled);
cout<<"tikets return ok."<<endl;
return;
}
至此,对与整个订票系统的介绍就到此为止,不过我还有一些想法还没有实现:
1. 把它改成网络版的
2. 希望能够找到一种简单的方式来解析命令,以实现更为强大的脚本引擎
3. System里面的一些方法要重写,users,plans,orders类要从system类里独立出来
运动会分数统计系统
任务:参加运动会有n个学校,学校编号为1……n。比赛分成m个男子项目,和w个女子项目。项目编号为男子1……m,女子m+1……m+w。不同的项目取前五名或前三名积分;取前五名的积分分别为:7、5、3、2、1,前三名的积分分别为:5、3、2;哪些取前五名或前三名由学生自己设定。(m<=20,n<=20)
功能要求:
1).可以输入各个项目的前三名或前五名的成绩;
2).能统计各学校总分,
3).可以按学校编号、学校总分、男女团体总分排序输出;
4).可以按学校编号查询学校某个项目的情况;可以按项目编号查询取得前三或前五名的学校。
规定:输入数据形式和范围:20以内的整数(如果做得更好可以输入学校的名称,运动项目的名称)
输出形式:有中文提示,各学校分数为整形
界面要求:有合理的提示,每个功能可以设立菜单,根据提示,可以完成相关的功能要求。
存储结构:根据系统功能要求自己设计,但是要求运动会的相关数据要存储在数据文件中。
3.3.1 运动会积分系统设计分析
这个程序主要是小组的另一位成员郑云飞写的,但基本的算法我们一起讨论的,就不详细说明了,下面简要介绍如下.
针对这一项目,首先要解决界面的问题,所以采用了VC6.0的编译环境,并立一个单文档结构,通过菜单的选择弹出对话框,然后在对话框中进行数据传输和修改,并将结果保存为一个相应的文件。
为了能将学校及项目信息组织起来,创建两个类,其中的数据包括所有的学校名称、男女项目名称、各个项目得分、当前男女项目总数、当前参与学校总数,其中学校名称和项目名称及相应的项目得分用数组保存,类型为CString和int类型,其他信息均为int类型。此外,为方便数据录入操作,在其中还加入了成员函数,来完成信息的录入。
在对话框的实现上,通过对相应消息的响应,来完成数据的操作。同时,在对文件进行读取和写入时,运用了MFC提供的CArchive类和CFile类,这样的好处是可以将类中的数据按照程序中的类型写入文件并读出。因此全程的文件操作只需要将文件内容读入一个相应的对象,并在对该对象操作完成后在都在文件进行保存即可。
3.3.2 运行环境以及编写语言
本程序用c++所写,用了vc的mfc库,用vc6.0编译,运行平台是windows。
。
4.1.3 定义的数据结构,各功能模块的源程序
(1)定义的类
自定义的信息存储类:
class School : public CObject
{
public:
CString name;
CString manObject [20];
CString womanObject [20];
int manObj [20];
int womanObj [20];
int numMan;
int numWoman;
School ()
{};
};
class SportMessage : public CObject
{
public:
SportMessage ();
School message [20];
int numSchool;
void SportMessage::mesInputMan ( SportMessage &obj, CString name, CString object , int namePos, int objPos, int record);
void SportMessage::mesInputWoman ( SportMessage &obj, CString name, CString object , int namePos, int objPos, int record);
void wInput ( SportMessage &obj, CString name, CString object, int record );
void mInput ( SportMessage &obj, CString name, CString object ,int record );
};
3.3.4 算法描述:
在细节上对数据的组织是通过数组和类进行的,同时在对从文件中读取的对象进行修改时,采用了引用传递参数的方法。按学校的编号输出时,则依据已有的学校个数,将其从一个CString的对象数组中顺序输出;按成绩输出时,则先将每个学校的男女成绩求和,然后将结果保存在数组中,在对其进行选择排序,按照由大到小的顺序,将将对象中对应的下标传入函数,并将相应的学校姓名传递给对话框中的相应资源,待全部完成后,将其结果显示
void SportMessage::mesInputMan ( SportMessage &obj, CString name, CString object , int namePos, int objPos, int record)
{
if ( obj.numSchool == 20 || obj.message [ namePos ].numMan == 20 )
{
AfxMessageBox (" 对不起你说录入的数据已超过规定数量(20个),所以将被忽略" );
return;
}
obj.message [namePos].name = name;
obj.message [namePos].manObject [objPos] = object;
obj.message [namePos].manObj [objPos] = record;
obj.message [namePos].numMan ++;
}
void SportMessage::wInput ( SportMessage &obj, CString name, CString object, int record )
{
char *temp = NULL;
int n = 0;
int t = 0;
name.TrimLeft ();
name.TrimRight ();
object.TrimLeft ();
for ( n ; n<20; n++ )
{
if ( obj.message [n].name.Compare ( name ) == 0 )
{
for ( t ; t<20; t++ )
{
if ( obj.message [n].womanObject [t].Compare ( object ) == 0 )
{
obj.message [n].womanObj [t] = record;
return;
}
else
if ( obj.message [n].womanObject [t].IsEmpty () )
{
mesInputWoman ( obj, name, object , n, t, record );
return;
}
}
}
else
if ( obj.message [n].name.IsEmpty () )
{
mesInputWoman ( obj, name, object , n, 0, record );
if ( obj.numSchool < 20 )
{
obj.numSchool ++;
}
return;
}
}
}
void SportMessage::mInput ( SportMessage &obj, CString name, CString object, int record )
{
char *temp = NULL;
int n = 0;
int t = 0;
name.TrimLeft ();
name.TrimRight ();
object.TrimLeft ();
for ( n ; n<20; n++ )
{
if ( obj.message [n].name.Compare ( name ) == 0 )
{
for ( t ; t<20; t++ )
{
if ( obj.message [n].manObject [t].Compare ( object ) == 0 )
{
obj.message [n].manObj [t] = record;
return;
}
else
if ( obj.message [n].manObject [t].IsEmpty () )
{
mesInputMan ( obj, name, object , n, t, record );
return;
}
}
}
else
if ( obj.message [n].name.IsEmpty () )
{
mesInputMan ( obj, name, object , n, 0, record );
if ( obj.numSchool < 20 )
{
obj.numSchool ++;
}
return;
}
}
}
成绩查询:
void FindRec::OnButton1()
{
// TODO: Add your control notification handler code here
CFile file;
SportMessage temp ;
file.Open("hj.dat",
CFile::modeCreate|CFile::modeNoTruncate|CFile::modeRead );
CArchive ar ( &file, CArchive::load );
for ( int n = 0; n<20; n++)
{
ar>>temp.message [n].name;
for ( int t = 0; t<20; t++ )
{
ar>>temp.message [n].manObject [t];
ar>>temp.message [n].womanObject [t];
ar>>temp.message [n].manObj [t];
ar>>temp.message [n].womanObj [t];
}
ar>>temp.message [n].numMan;
ar>>temp.message [n].numWoman;
}
ar>>temp.numSchool;
ar.Close ();
file.Close ();
UpdateData ( TRUE );
m_edit1.MakeLower ();
m_edit2.MakeLower ();
m_edit3.MakeLower ();
if ( m_radio1 == 0 )
{
m_edit3.Empty ();
m_edit4.Empty ();
m_edit6.Empty ();
m_edit8.Empty ();
m_edit9.Empty ();
m_edit10.Empty ();
m_edit1.TrimLeft ();
m_edit1.TrimRight ();
m_edit2.TrimLeft ();
m_edit2.TrimRight ();
CString school, object;
school = m_edit1;
object = m_edit2;
for ( int n = 0; n < temp.numSchool; n++ )
{
if ( temp.message [n].name.Compare ( school ) == 0 )//如果有这个学校
{
if ( m_radio5 == 0 )
{
for ( int t = 0; t < temp.message [n].numMan; t++ )
{
if ( temp.message [n].manObject [t].Compare ( object ) == 0 )//如果存在这个男子项目
{
char *mes = new char [5] ;
itoa ( temp.message [n].manObj[t], mes, 10 );
m_edit3 += mes ;
UpdateData ( FALSE );
delete [] mes;
return ;
}
}
m_edit3.Empty ();
UpdateData ( FALSE );
MessageBox ( "不存在这个男子项目" );
return;
}
else
{
for ( int t = 0; t < temp.message [n].numWoman; t++ )
{
if ( temp.message [n].womanObject [t].Compare ( object ) == 0 )//如果存在这个女子项目
{
char *mes = new char [5];
itoa ( temp.message [n].womanObj[t], mes, 10 );
m_edit3 += mes ;
UpdateData ( FALSE );
delete [] mes;
return ;
}
}
m_edit3.Empty ();
UpdateData ( FALSE );
MessageBox ( "不存在这个女子项目" );
return;
}
}
}
m_edit3.Empty ();
UpdateData ( FALSE );
MessageBox ( "不存在这个学校" );
return;
}
else
{
m_edit1.Empty ();
m_edit3.Empty ();
m_edit4.Empty ();
m_edit6.Empty ();
m_edit8.Empty ();
m_edit9.Empty ();
m_edit10.Empty ();
m_edit2.TrimLeft ();
m_edit2.TrimRight ();
int position [15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };//保存位置及得分
int pos = 0; //前五个为学校位置,中间五个为项目位置,后五个为得分
for ( int n = 0; n < temp.numSchool; n++ )
{
if ( m_radio5 == 0 )
{
for ( int t = 0; t < temp.message [n].numMan; t++ )
{
if ( temp.message [n].manObject[t].Compare ( m_edit2 ) == 0 )//如果存在这个项目
{
position [ pos ] = n;
position [ pos + 5 ] = t;
int k = 0;
k = temp.message [n].manObj [t];
position [ pos + 10 ] = k;
pos ++;
break;
}
}
}
else
{
for ( int t = 0; t < temp.message [n].numWoman; t++ )
{
if ( temp.message [n].womanObject[t].Compare ( m_edit2 ) == 0 )//如果存在这个项目
{
position [ pos ] = n;
position [ pos + 5 ] = t;
position [ pos + 10 ] = temp.message [n].womanObj [t];
pos ++;
break;
}
}
}
}
if ( pos != 3 && pos != 5 )
{
UpdateData ( FALSE );
MessageBox ( "不存在这个项目或该项目信息不完整! " );
return ;
}
else
{
int rec = 0, sign = 0;
CString school [5];
for ( n = 0; n < pos; n++ )
{
rec = position [ n + 10 ];
sign = ( n + 10 );
for ( int t = 0; t <=pos; t++ )
{
if ( rec < position [ t + 10 ] )
{
rec = position [ t + 10 ];
sign = ( t + 10 );
}
}
if ( m_radio5 == 0)
{
school [n] = temp.message [ position [sign - 10] ].name;
}
else
{
school [n] = temp.message [ position [sign - 10] ].name;
}
position [ sign ] = 0;
}
if ( pos == 3 )
{
m_edit4 = school [0];
m_edit6 = school [1];
m_edit8 = school [2];
}
else
{
m_edit4 = school [0];
m_edit6 = school [1];
m_edit8 = school [2];
m_edit9 = school [3];
m_edit10 = school [4];
}
}
}
UpdateData ( FALSE );
}
3.3.5 界面设计:
(图4)
错误信息的显示:
··