个人账单管理系统数据结构课程设计 精品.docx
《个人账单管理系统数据结构课程设计 精品.docx》由会员分享,可在线阅读,更多相关《个人账单管理系统数据结构课程设计 精品.docx(21页珍藏版)》请在冰豆网上搜索。
个人账单管理系统数据结构课程设计精品
沈阳航空航天大学
课程设计报告
课程设计名称:
数据结构课程设计
课程设计题目:
个人账簿管理系统
目录
沈阳航空航天大学I
1课程设计介绍1
1.1课程设计内容1
1.2课程设计要求1
2课程设计原理2
2.1课设题目粗略分析2
2.2原理图介绍2
2.2.1功能模块图2
2.2.2流程图分析3
3数据结构分析7
3.1存储结构7
3.2算法描述7
4.1调试过程16
4.2程序执行过程16
参考文献19
1课程设计介绍
1.1课程设计内容
个人账簿管理系统记录某人每月的全部收入及各项开支情况,包括食品消费,房租,子女教育费,水电费,医疗费,储蓄等。
进入系统后可以输入和修改某月的收支情况,可以对每月的开支从小到大进行排序,可以根据输入的月份查询每月的收支情况。
1.2课程设计要求
1.采用链表结构管理收支情况,并能够保存到文件中;
2.完成对每月的开支排序,以及完成系统查询功能;
3.独立完成系统的设计,编码和调试;
4.系统利用C语言实现;
5.按照课程设计规范书写课程设计报告。
2课程设计原理
2.1课设题目粗略分析
根据课设题目要求,拟将整体程序分为五大模块。
以下是五个模块的大体分析:
1.账簿信息输入:
该模块中,以带头结点的链表为账单信息的存储结构,输入各项开支的数据。
2.账簿信息排序:
该模块中,将针对每月的消费总额进行从小到大的顺序进行排序。
3.账簿信息查询:
该模块中,将输入或已排序账单进行按所输入的日期进行查询并将查询结果打印至屏幕,供人阅读。
4.账单信息更改:
进入该模块,进行信息的更改或添加。
5.账单信息保存:
该模块将账单信息以文本文档保存到磁盘中。
2.2原理图介绍
2.2.1功能模块图
图2.1功能模块图
2.2.2流程图分析
1.账单建立时调用jianli()函数,而在在jianli()中调用tianjia()函数;账单的头结点的申请工作已在主函数中完成,所以在整个建立账单的过程中核心内容是:
将新的账单信息链接到已存在的账单后面;tianjia()函数流程图如图2.2所示。
图2.2账单信息添加函数流程图
2.在对账单信息进行排序的过程中,采用的方法是:
首先将账单中支出最大和最小的两个结点寻找出来并按从小到大的顺序连接到一个新申请的头结点上;再将原函数的各结点插入到新的链表中,最终新的链表就成为了排序后的新账单。
paixu()函数流程图如图2.3所示。
图2.3排序函数流程图
3.进入账单信息查询部分,程序根据输入的日期进行逐一比较,若存在该日期的账单信息,程序输出相应的账单信息,若不存在该日期的账单信息,系统提示用户还未录入该日期的账单信息。
账单查询函数流程图如图2.4所示。
图2.4查询函数流程图
4.进入账单更改部分,程序将用户输入的需更改的账单日期与已存在的账单日期进行逐步比对,对日期完全相同的账单信息进行更改,若不存在该日期的账单信息,程序提示用户是否马上进行添加。
流程图如图2.5所示。
图2.5账单信息更改流程图
5.进入账单保存部分,用户对建立的账单以文本文档的形式保存到磁盘中,账单信息保存函数流程图如图2.6所示。
图2.6保存账单信息函数流程图
3数据结构分析
3.1存储结构
主要存储结构:
带头结点的单向链表存储结构。
typedefstructMYBILL//链表结点结构
{
longdate;//日期信息
longfood;//食品消费
longfangzu;//房租费用
longjiaoyu;//子女教育费
longshuidian;//水电费
longyiliao;//医疗费
longchuxu;//储蓄
longpay;//总支出
longincome;//总收入
structMYBILL*next;//指向下一结点的指针
}MYBILL,bill;
3.2算法描述
1.建立单向链表,用来存储账单信息:
首先分别输入将要建立账单的年份及月份,年份的合理范围定为所有正整数,当年份输入为非合理数字时结束对账单的录入;月份的合理范围为1至12,当输入不属于该范围的月份数据时,程序会重新进行年份和月份的录入;录入日期数据后,进行其余信息的录入。
void*tianjia(MYBILL*head)//向原链表添加新的链表结点
{inti,j;//定义年份和月份变量
MYBILL*p,*tail;//定义结构体类型指针
p=head;//将指针p初始化为head
while(p->next!
=NULL)//判断账单链表是否为仅含空头结点的链表
{p=p->next;//将指针p向后移动一个单位
}
printf("\n\t输入您要建立账单的年份,当输入非正数时结束输入!
\n\t");
scanf("%ld",&i);//输入年份数据
while(i>0)//判断输入的年份是否为合理数据
{printf("\n\t输入您要建立账单的月份\n\t");
scanf("%d",&j);//输入月份数据
if(j>0&&j<13)//判断输入的月份数据是否为合理数据
{tail=(bill*)malloc(sizeof(bill));//申请一个结点空间
if(tail==NULL)
{printf("存储空间分配失败!
\n");
return(NULL);
}
tail->date=100*i+j;//将年份和月份为转为一长整数字存储
tail->next=NULL;//新结点的指针域指向空
printf("\n\t输入食品费用:
\t");
scanf("%ld",&tail->food);//输入食品费用
printf("\n\t输入房租:
\t");
scanf("%ld",&tail->fangzu);//输入房租费用
printf("\n\t输入子女教育费:
");
scanf("%ld",&tail->jiaoyu);//输入子女教育费
printf("\n\t输入水电费:
\t");
scanf("%ld",&tail->shuidian);//输入水电费
printf("\n\t输入医疗费:
\t");
scanf("%ld",&tail->yiliao);//输入医疗费
printf("\n\t输入储蓄:
\t");
scanf("%ld",&tail->chuxu);//输入存储
printf("\n\t输入总收入:
\t");
scanf("%ld",&tail->income);//输入总收入tail->pay=tail->food+tail->fangzu+tail->jiaoyu+tail->shuidian+tail->yiliao;
//将所有支出相加作为总支出
p->next=tail;//将新申请的结点链接到原链表的最尾端
p=p->next;//指针p向后移动一个单位
}
elseprintf("\n\t输入月份不合理,请重新输入!
\n\t");
printf("\n\t输入您要建立账单的年份,当输入非正数时结束输入!
\n\t");
scanf("%ld",&i);//输入年份信息
}
}
MYBILL*jianli(MYBILL*head)//建立账单链表函数
{
MYBILL*head;//定义结构体类型指针
tianjia(head);//调用子函数tianjia()
if(head->next!
=NULL)//判断账单链表是否为仅含空头结点的链表
printf("\n\t\t\t===========账单录入成功!
===========\n\n");
else
printf("\n\t\t\t===========没有账单录入!
===========\n\n");
returnhead;//返回头指针
}
2.账单排序,排序方法采用将所有数据中最小和最大值挑选出来,再将处于这两个数据中间位置的其他数据进行插入。
MYBILL*paixu(MYBILL*head)//账单信息排序函数
{longmin,max;//定义两个变量对两个最值数据进行标记
inti,j;//年份月份定义
MYBILL*q,*t,*p,*head1;//定义结构体类型指针
q=head->next;//将q初始化为head->next
if(head->next!
=NULL)//判断链表是否为只含头结点的空链表
{min=q->pay;//第一结链表支出最少
t=head;//初始化指针t的值
p=q;//初始化p的值
while(q->next!
=NULL)//月支出最小结点为新排序账单的第一个结点
{if(min>q->next->pay)//比较q的pay和q->next的pay大小
{min=q->next->pay;//如果p->next的pay小将其值赋给min
p=q->next;//用p标记支出最少的结点
t=q;//用t标记支出最少的结点的上一结点
}以便摘除支出最少的结点
q=q->next;//将指针q向后移动一个单位
}
t->next=p->next;//将支出最少的结点摘下
head1=(bill*)malloc(sizeof(bill));//申请排序后链表头结点
head1->next=p;//将最小支出连接到新链表后作为第一个结点
p->next=NULL;//将新链表尾端指向空
q=head->next;//再次将q指向原链表头指针的后一结点
if(q!
=NULL)//判断链表是否为只含头结点的空链表
{max=q->pay;//月支出最大结点为新排序账单的第二个结点
t=head;//初始化指针t的值
p=q;//初始化p的值
while(q->next!
=NULL)//判断是否只存在最大最小支出外
{if(maxnext->pay)//比较max与q->next->pay的大小
{max=q->next->pay;//如果p->next的pay小将其值赋给max
p=q->next;//用指针p标记支出最多的结点
t=q;//用指针t标记支出最大的结点
}以便摘除支出最多的结点
q=q->next;//将指针q向后移动一个单位长度
}
t->next=p->next;//摘掉支出最大的结点
head1->next->next=p;//将最大支出连接到新链表后
p->next=NULL;//将新链表尾指向空
q=head->next;//第三次初始化指针q
while(q!
=NULL)//依次从原表中摘取结点插入到新表,使其有序
{head->next=q->next;//将q结点摘除
p=head1->next;//初始化p指向新链表头结点的下一结点
while(q->pay>p->next->pay)//比较原链表与新链表支出
{p=p->next;//将新链表的p结点向后移动
}
q->next=p->next;//将q结点链接到p后
p->next=q;
q=head->next;//将q重新置于head后结点上
}
}
head=head1;//将新链表头指针赋给原链表头指针
p=head->next;//初始化p
while(p!
=NULL)//输出排序后的结果
{i=p->date/100;
j=p->date%100;
printf("\n\t\t日期:
%d年%d月\n\t\t食品费用:
%ld\n\t\房租:
%ld\n\t\t子女教育费:
%ld\n\t\t水电费:
%ld\n\t\t医疗费:
%ld\n\t\t储蓄:
%ld\n\t\t总支:
%ld\n\t\t收入:
%ld\n",i,j,p->food,p->fangzu,p->jiaoyu,p->shuidian,p->yiliao,p->chuxu,p->pay,p->income);
p=p->next;
}
}
else
printf("\n\t您还未建立账单,无法执行排序及查看操作!
\n");
returnhead;
}
3.输入将要查询的日期,程序将输入的数据转化与账单信息比较,将符合的账单信息输出。
voidchaxun(MYBILL*head)//查询单月账单函数
{longk;
inti,j,m=0;
MYBILL*p;
if(head->next!
=NULL)//判断账单是否为空账单
{printf("\n\t输入0退出查询,请输入您要查询的年份:
");
scanf("%d",&i);//输入查询的年份
while(i>0)//判断输入年份为合理数据
{m=0;//使用m标记查询的日期是否存在
p=head->next;
printf("\n\t请输入您要查询的月份:
");
scanf("%d",&j);//输入查询的月份
if(j>0&&j<13)//判断查询的月份是否合理
{k=100*i+j;//将输入的年份月份进行转化
while(p!
=NULL)
{if(k==p->date)//判断是否存在输入的日期信息
{m++;//变化m值,以表示存在记录
printf("\n\t\t日期:
%d年%d月\n\t\t食品费用:
%ld\n\t\t房租:
%ld\n\t\t子女教育费:
%ld\n\t\t水电费:
%ld\n\t\t医疗费:
%ld\n\t\t储蓄:
%ld\n\t\t总支出:
%ld\n\t\t收入:
%ld\n",i,j,p->food,p->fangzu,p->jiaoyu,p->shuidian,p->yiliao,p->chuxu,p->pay,p->income);
break;
}
p=p->next;
}
if(m==0)//依据m值判断是否查询成功
printf("\n\t您查询的日期无记录!
\n");
}
else
printf("\n\n\t您输入的月份不合理!
\n");
printf("\n\t输入0退出查询,请输入您要查询的年份:
");
scanf("%ld",&i);
}
}
else
printf("\n\t您还没建立账单!
\n");
}
4.输入需更改的账单日期,进行更改,若不存在记录,程序设有添加功能。
MYBILL*genggai(MYBILL*head)//更改账单函数
{longi,j,k;
intm,s;
MYBILL*p;
p=head->next;
printf("\n\t输入0退出更改,请输入您要更改的年份:
");
scanf("%ld",&i);//输入更改账单的年份
while(i>0)//判断年份合理与否
{m=0;//使用m标记是否存在该日期账单记录
printf("\n\t请输入您要更改的月份:
");
scanf("%ld",&j);//输入更改账单的
if(j>0&&j<13)//判断月份的合理性
{k=100*i+j;//将输入的日期转化为一长整形数字
while(p!
=NULL)//判断账单是否为空
{if(k==p->date)
{printf("\n\t输入食品费用:
\t");//输入更改后的账单信息
scanf("%ld",&p->food);
printf("\n\t输入房租:
\t");
scanf("%ld",&p->fangzu);
printf("\n\t输入子女教育费:
");
scanf("%ld",&p->jiaoyu);
printf("\n\t输入水电费:
\t");
scanf("%ld",&p->shuidian);
printf("\n\t输入医疗费:
\t");
scanf("%ld",&p->yiliao);
printf("\n\t输入储蓄:
\t");
scanf("%ld",&p->chuxu);
printf("\n\t输入总收入:
\t");
scanf("%ld",&p->income);
p->pay=p->food+p->fangzu+p->jiaoyu+p->shuidian+p->yiliao;
m++;//改变m值,标记已更改账单信息
break;
}
p=p->next;
}
if(m==0)//判断是否更改成功
{printf("\n\t您要更改的日期无记录!
是否立即添加?
(1--是,其余键--否)");
scanf("%d",&s);//输入提示的操作,选择是否添加账单信息
if(s==1)tianjia(head);//调用子函数技进行账单信息添加
}
}
elseprintf("\n\n\t您输入的月份不合理!
\n");
printf("\n\t输入0退出更改,请输入您要更改的年份:
");
scanf("%ld",&i);
}
returnhead;
}
6.以文本文档形式保存账单信息,保存路径为程序所在位置。
voidxieru(MYBILL*head)
{FILE*fp;
MYBILL*p;
p=head->next;
charch,filename[10];
if(head->next!
=NULL)
{printf("\n\t输入将要保存的文件名:
");
scanf("%s",filename);//输入保存的文件名
if((fp=fopen(filename,"w"))==NULL)
{printf("\n不能打开!
\n");
exit(0);
}
ch=getchar();//接受文件名输入后的回车符
while(p!
=NULL)//判断账单是否为空
{fprintf(fp,"日期:
%ld,食品费用:
%ld,房租:
%d,子女教育费:
%ld,水电费:
%ld,医疗费:
%ld,储蓄:
%ld,总收入:
%ld",p->date,p->food,p->fangzu,p->jiaoyu,p->shuidian,p->yiliao,p->chuxu,p->income);
p=p->next;
}
fclose(fp);//关闭文件
}
else
printf("\n\t您未建立新账单,无法保存!
\n");
}
4.1调试过程
在调试程序是主要遇到的问题是:
在程序最开始运行时,如果还没有建立账单信息,则此时查询、查看、更改和保存时由于没有头指针传入子函数,程序会出现错误而停止运行。
解决方法:
采用使用带有头结点的链表存储账单信息,在程序运行时即刻申请头结点,在进入子函数后,对头结点后是否为空进行判断来判断是否已经录入账单信息,从而避免了问题的再次发生。
4.2程序执行过程
运行程序,运行结果如下图
输入1,选择账单录入功能,结果如下图
输入账单的日期为2012年1月,分别输入各项开支,如下图
录入账单完成后,选择查询功能,如下图
输入查询日期2012年1月,运行结果如下图
返回主菜单后,选择账单存储,如下图
输入保存的文本文件名为201201,如下图
文本文件保存结果如下图
参考文献
[1]严蔚敏,吴伟民.数据结构[M].北京:
清华大学出版社,2007.
[2]张长海,陈娟.C程序设计[M].北京:
高等教育出版社,2004.
[3]谭浩强.C程序设计[M].北京:
清华大学出版社,2005.
[4]朱国进。
程序设计与问题求解.北京:
东华大学出版社,2004.
[5]陈国良。
.并行计算——结构·算法.北京:
高等教育出版社,1999
[6]石峰。
.程序设计基础.北京:
清华大学出版社,2003
[7]郝玉洁等.C语言程序设计.北京:
机械工业出版社,2000
[8]高克宁等。
.C语言程序设计.北京:
科学出版社,2003
课程设计总结:
通过这次课程设计,使我对数据结构有了更加深入的认识,尤其在链表的建立和使用方面又有了显著的巩固和提高,同时巩固了大一年学习的知识,尽管在设计过程中遇到了一些困难,但通过查阅资料、请教老师、请教学长和同学的方法,把一个又一个问题解决,成功地完成了课设题目。
这次课程设计练习了我的动手能力,让我意识到平时的上机练习是多么的重要,同时,我更加的相信,只要努力就一定会有收获,只有掌握扎实的知识,获得成功才会更加容易,因此,在进后的学习中我会加倍努力,从而令我的编程努力得到更大的提升,让我编出的程序更加的完美。
指导教师评语:
指导教师(签字):
年月日
课程设计成绩