中北大学
数据结构与算法课程设计
说明书
2015 年1月 29 日
1 需求分析
1) 每条信息至包含 :姓名(NAME )街道(STREET)城市(CITY)邮编(EIP)国家(STATE)几项
2) 作为一个完整的系统,应具有友好的界面和较强的容错能力
3) 上机能正常运行,并写出课程设计报告
通讯录的基本活动包括:对一个人的采编、删除、查找和显示等等。由于上述四项基本活动都是通过人名(即关键字)进行的。
作为通讯录,就需要一个模块来完成对别人的登记和记录情况,本程序使用文件来完成上述操作。
2 设计内容
本系统应完成一下几方面的功能:
1) 输入信息——enter();
2) 显示信息———display( );
3) 查找以姓名作为关键字 ———search( );
4) 删除信息———delete( );
5) 存盘———save ( );
6) 装入———load( ) ;
3 设计目的
用〈〈数据结构〉〉中的双向链表作数据结构,结合C语言基本知识。编写一个通讯录管理系统。以把所学数据结构知识应用到实际软件开发中去。
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素。
因此,为了表示每个数据元素与其后继元素之间的逻辑关系,对于数据元素来说,除了存储数据本身信息之外,还需要存储一个指示其后继的信息。
这两部分组成数据的存储映像,称为结点。
4.系统流程图
5.详细设计及
(1) 结构体:
(构造一个结构体来存储和使用数据)
struct address{ /*定义结构*/
char name[30]; //姓名
char street[100]; //街道
char city[30]; //城市
char state[30]; //国家
char zip[11]; //邮政编码
struct address *next; /*后继指针*/
struct address *prior; /*前导指针*/
};
struct address *start; /*首结点*/
struct address *last; /*尾结点*/
struct address *find(char *); /*声明查找函数*/
(2)包含被调用函数:
功能
void enter(); //输入信息 /*函数声明*/
void search(); //查找信息
void save(); //存盘
void load(); //装入
void list(); //显示信息
void mldelete(struct address **,struct address **); //删除信息
void dls_store(struct address *i,struct address **start,
struct address **last);
void inputs(char *,char *,int);
void display(struct address *);
int menu_select(void);
(3)实现主程序与各模块的调用关系:
主函数通过调用各个函数来连接各个函数,从而实现程序功能的实现。
int main(void)
{
start = last = NULL;
for(;;)
{
switch(menu_select())
{
case 1:enter();
continue;
case 2:mldelete(&start,&last);
continue;
case 3:list();
continue;
case 4:search();
continue;
case 5:save();
continue;
case 6:load();
continue;
case 7:exit(0);
}
}
}
6.部分调试界面
7.程序源码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct address{ /*定义结构*/
char name[30];//姓名
char street[100];//街道
char city[30];//城市
char state[30];//国家
char zip[11];//邮编
struct address *next; /*后继指针*/
struct address *prior; /*前导指针*/
};
struct address *start; /*首结点*/
struct address *last; /*尾结点*/
struct address *find(char *); /*声明查找函数*/
void enter(); /*函数声明*/
void search();
void save();
void load();
void list();
void mldelete(struct address **,struct address **);
void dls_store(struct address *i,struct address **start,
struct address **last);
void inputs(char *,char *,int);
void display(struct address *);
int menu_select(void);
int main(void)
{
start = last = NULL;
for(;;)
{
switch(menu_select())
{
case 1:enter();
continue;
case 2:mldelete(&start,&last);//删除
continue;
case 3:list();//显示
continue;
case 4:search();//查找
continue;
case 5:save();//保存
continue;
case 6:load();//装入
continue;
case 7:exit(0);//退出
}
}
}
int menu_select(void) /*主目录*/
{
char s[80];
int c;
printf("……………………~欢迎使用迷你通讯录系统~………………………\n");
printf("*****************************************\n");
printf("************** 1.输入信息 ***************\n");
printf("************** 2.删除信息 ***************\n");
printf("************** 3.显示信息 ***************\n");
printf("************** 4.查找 ***************\n");
printf("************** 5.存盘 ***************\n");
printf("************** 6.装入 ***************\n");
printf("************** 7.退出 ***************\n");
printf("*****************************************\n");
do{
printf("\n请选择:\n");
gets(s);
c = atoi(s);//是把字符串转换成长整型数的一个函数
}while(c<0||c>7); /*超出选项范围时,提示重新输入*/
return c; /*返回输入值*/
}
void enter() /*输入函数,本函数循环输入资料,当输入姓名为空时退出*/
{
struct address *info; /*定义当前结点*/
for(;;)
{
info=(struct address *)malloc(sizeof(struct address)); /*为当前结点分配空间*/
if(!info)
{
printf("\n 分配空间失败");
exit(0); /*如果分配空间失败,退出程序*/
}
printf("输入空姓名结束:\n");
inputs("姓名:",info->name,30);
if(!info->name[0])
break; /*如果输入姓名为空,结束循环*/
inputs("街道:",info->street,100);
inputs("城市:",info->city,30);
inputs("国家:",info->state,30);
inputs("邮编:",info->zip,11);
dls_store(info,&start,&last); /*调用结点插入函数*/
}
}
void inputs(char *prompt,char *s,int count) /*输入函数,有越界检测功能*/
{
char p[255];
do
{
printf(prompt);
fgets(p,254,stdin);
if(strlen(p)>count)
printf("\n太长\n");
}while(strlen(p)>count);
p[strlen(p)-1]=0;
strcpy(s,p);
}
void dls_store( /*数据插入函数,也是本例的关键函数*/
struct address *i, /*接受传入的当前输入结点地址*/
struct address **start, /*接受传入的首结点地址*/
struct address **last /*接受传入的尾结点地址*/
)
{
struct address *old,*p;
if(*last==NULL) /*如果尾结点为空,意味着当前链表为空*/
{
i->next=NULL;
i->prior=NULL;
*last=i;
*start=i;
return;
}
/*如果链表不为空*/
p=*start; /*p取入口地址(也就是首结点地址)*/
old=NULL; /*old赋空*/
while(p)
{ /*如果p不为空时,执行特循环体,查找比当前结点应该插入的位置*/
if(strcmp(p->name,i->name)<0) /*如果当前结点的name域比p(首结点)大时(实现以name域升序排序)*/
{
old=p;
p=p->next;
}
else /*如果当前输入数据中的name域比p小时,把数据插入结点p之前*/
{
if(p->prior) /*如果p的前驱不为空时*/
{
p->prior->next=i;
i->next=p;
i->prior=p->prior;
p->prior=i;
return;
}
i->next=p; /*如果p的前驱为空时,把当前结点作为首结点,并令当前结点的后驱为p*/
i->prior=NULL;
p->prior=i;
*start=i;
return;
}
} /*循环体结束*/
old->next=i; /*如果在整个链表都找不到name域比当前数据的name域大的结点,
*把当前数据放在作为尾结点放在最后*/
i->next=NULL;
i->prior=old;
*last=i; /*尾结点取i地址*/
}
void mldelete(struct address **start,struct address **last) /*删除函数*/
{
struct address *info;
char s[80];
inputs("要删除的姓名:",s,30); /*输入欲删除结点的name域内容*/
info=find(s);
if(info)
{
printf("删除ing......\n");
if(*start==info) /*如果该结点为首结点,把该结点的下驱作为新的首结点(入口)*/
{
*start=info->next;
if(*start)
(*start)->prior=NULL; /*如果新入口不为空,把入口的前驱置空*/
else *last=NULL; /*如果新入口为空,把尾结点置空,链表为空*/
}
else /*如果欲删除的结点不是首结点*/
{
info->prior->next=info->next;
if(info!=*last) /*如果该结点是尾结点,则令该结点的前驱为尾结点*/
info->next->prior=info->prior;
else
*last=info->prior;
}
free(info);
printf("删除成功!\n");
}
}
struct address *find(char *name) /*查找函数,形参为欲查找结点的name域*/
{
struct address *info;
info=start;
while(info)
{
if(!strcmp(name,info->name))return info;
info=info->next;
}
printf("没有找到这个名字!\n");
return NULL;
}
/*输出整个链表*/
void list(void)
{
struct address *info;
info=start;
if(info==NULL)
printf("没有信息");
while(info)
{
display(info);
info=info->next;
}
printf("\n\n");
}
void display(struct address *info) /*输出传入结点函数*/
{
printf(" -----------------------------------------------------------------------------\n");
printf(" %-18s%-18s%-18s%-15s%s\n","姓名","街道","城市","邮编","国家");//格式控制输出
printf(" %-18s%-18s%-18s%-15s%s\n",info->name,info->street,info->city,info->zip,info->state);
/*printf("%s\n",info->name);
printf("%s\n",info->street);
printf("%s\n",info->city);
printf("%s\n",info->state);
printf("%s\n",info->zip);*/
//printf("\n\n");
}
void search(void) /*查找函数*/
{
char name[40];
struct address *info;
printf("输入想要查找的姓名:"); /*输入欲查找的姓名*/
gets(name);
info=find(name);
if(!info)
printf("没有找到\n"); /*如果没找到,显示Not found*/
else
display(info); /*如果找到,显示该结点资料*/
}
void save(void) /*保存函数*/
{
struct address *info;
FILE *fp;
fp=fopen("mlist","wb"); /*生成文件*/
if(!fp)
{
printf("不能打开文件.\n");
return;
}
printf("\n正在保存 ……\n");
info=start;
while(info) /*把链表写入文件*/
{
fwrite(info,sizeof(struct address),1,fp);
info=info->next;
}
printf("-ok!\n");
fclose(fp);/*链表全部写入文件后,关闭文件*/
}
void load() /*调用预存文件函数*/
{
register int t, size; //register变量的值是存放在CPU中的寄存器中,调用时直接从寄存器中取出参加运算,存放在寄存器中的变量值调用需要的时间短
struct address *info,*temp=0;
char *p;
FILE *fp; /*打开文件*/
if((fp=fopen("mlist","r"))==NULL)
{
printf("没有打开文件!\n");
exit(0);
}
printf("\n\n正在加载...\n"); /*调用文件*/
size=sizeof(struct address); /*为结点分配内存*/
start=(struct address*)malloc(size);
if(!start) /*如果读取失败,返回*/
{
printf("Out of memory!1\n");
exit(0);
}
info=start;
p=(char*)info;
while((*p++=getc(fp))!=EOF)
{
for(t=0;t<size-1;++t)
*p++=getc(fp);
info->next=(struct address*)malloc(size);
if(!info->next)
{
printf("Out of memory!2\n");
return;
}
info->prior=temp;
temp=info;
info=info->next;
p=(char*)info;
}
temp->next=0;
last=temp;
start->prior=0;
fclose(fp); /*关闭文件,释放内存*/
printf("-OK!\n");
}
8 心得体会
这次的程序实设计实验是对我们进入大学以来学习程序设计语言结果的一次大检验。自己动手,自己发现和解决问题。发现了自己的许多不足。平时没有掌握好的知识在这次实验中彻底暴露出来,经过不断思考,不断查阅资料和上机运行,解决其中大部分问题,当然还存在一些问题没有解决。我相信在以后的学习能够解决好它们。但是,收获还是不小的,我不仅对C的操作有了进一步的掌握,还了解到了程序设计的书写风格及其注释的格式。
书上和老师教的内容是有限的,我们需要不断地靠自己去学习,向他人请教,了解和掌握更多的知识,这样我们才能编出更好的C程序。而且程序的编写应是:三分编写,七分调试。
程序编写之前需求分析,至关重要,将关系这整个项目的成败。一名优秀程序员的成长,需要付出很多很多,编程是每天必做,所以在今后的编程之中,尽可能把基本技能练习熟练。做软件最终是满足用户的需求,所以做软件时应一切应以用记为导向。
总体来说,这次C语言程序设计实验还是比较成功的,虽然最终程序还存在一些不足,但能取得这样的成绩我还是比较高兴的。
在本次课程设计的制作过程中,全组成员都学习了很有关的知识。这样的项目对我们学习数据结构,C语言,是一个综合性很高的实践。一些以前没有学的很杂实的课程内容,也得到的很好的完善。
由于我们的知识浅薄,经验不足及阅历颇浅,因此,在该程序的设计方面还有很多的不足,会在以后的学习过程中,根据所学的知识不断的修改、完善,争取慢慢趋于完美。
在此感谢,在本程序的设计过程和报告编写过程中,帮助过我的同学以及老师。