数据结构课程设计通讯录、电话号码查询系统

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一、课程设计概述:
本次数据结构课程设计共完成两个题:电话号码查询系统、通讯录。

使用语言:C
编译环境:VC6.0
二、课程设计题目一
[实验内容]
电话号码查询系统
[问题描述]
设计散列表实现电话号码查找系统。

[需求分析]
(1)设每个记录有下列数据项:电话号码、用户名、地址;
(2)从键盘输入各记录,分别以电话号码和用户名为关键字建立散列表;
(3)采用一定的方法解决冲突;
(4)查找并显示给定电话号码的记录;
(5)查找并显示给定用户名的记录。

整个系统必须满足系统功能要求;设计不同的散列函数,比较冲突率;在散列函数确定的前提下,尝试各种不同类型处理冲突的方法,考察平均查找长度的变化。

[概要设计]
void getin(Record* a) // 键盘输入联系人的信息
void ShowInformation(Record* a) //显示输入的用户信息
Status collision(int p,int &c) // 冲突处理函数,采用二次探测再散列法解决冲突
void CreateHash(HashTable* H ,Record* a) // 建表,若哈希地址冲突,进行冲突处理void SearchHash ( HashTable* H,int &c) //在通讯录里查找关键字
void Save() //保存
void main_menu()
[存储结构]
typedef struct{//记录
NA name;
NA tel;
NA add;
}Record;
typedef struct{//哈希表
Record *elem[HASHSIZE]; //数据元素存储基址
int count; //当前数据元素个数
int size; //当前容量
}HashTable;
[详细设计]
#include<iostream> //cout,cin语句的头文件
#include<stdlib.h> //清屏函数头文件:使用csl时调用system
#include<string> //字符串头文件
#include<stdio.h>
#include<fstream>
#define MAXSIZE 100 //电话薄记录的数量
#define MAX_SIZE 50 //用户名、电话号码、地址的最大长度
#define HASHSIZE 400 //定义表长
#define SUCCESS 1 //查找
#define UNSUCCESS -1
#define LEN sizeof(HashTable) // 哈希表的长度
using namespace std;
typedef int Status;//typedef用来定义类型的别名。

此处用status作为int别名,目的表达int 变量是一个状态变量。

typedef char NA[MAX_SIZE]; //NA作为char的别名
typedef struct{ // 自定义一个记录用户名、电话号码、联系地址的结构体的别名record
NA name,tel,add,way;
}Record;
Record a[HASHSIZE];
typedef struct{ //散列表
Record *elem[HASHSIZE]; //数据元素存储地址
int count; //数据元素个数
int size; //容量
}HashTable;
Status eq(NA x,NA y)
{ //关键字比较,相等返回SUCCESS;否则返回UNSUCCESS
if(strcmp(x,y)==0)//2个字符串的大小比较s1=s2,strcmp(s1,s2) == 0; s1>s2, strcmp(s1,s2) == 1; s1<s2, strcmp(s1,s2) == -1;
return SUCCESS;
else
return UNSUCCESS;
}
Status NUM_BER; //记录的个数
void getin(Record* a){ // 键盘输入联系人的信息,Record*调用Record函数;a是参数
cout<<"请输入要添加的联系人的个数:\n";
cin>>NUM_BER;
int i;
for(i=0;i<NUM_BER;i++){
cout<<"请输入第"<<i+1<<"个记录的用户名:\n";
cin>>a[i].name;
cout<<"请输入第"<<i+1<<"个记录的电话号码:\n";
cin>>a[i].tel;
cout<<"请输入第"<<i+1<<"个记录的地址:\n";
cin>>a[i].add;
}}
void ShowInformation(Record* a)//显示输入的用户信息
{
int i;
for( i=0;i<NUM_BER;i++)
cout<<"\n第"<<i+1<<"个用户信息:\n 姓名:"<<a[i].name<<"\n 电话号码:"<<a[i].tel<<"\n 联系地址:"<<a[i].add<<"\n--------\n";
}
long fold(NA s) //人名的折叠处理:将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址
{
char *p;
long sum=0;
NA ss;
strcpy(ss,s);//复制字符串,不改变原字符串的大小写
strupr(ss);//将字符串ss转换为大写形式
p=ss;
while(*p!='\0')
sum+=*p++;
return sum;
}
int Hash1(NA str){//哈希函数
long n;
int m;
n=fold(str);//先将用户名进行折叠处理
m=n%HASHSIZE; //折叠处理后的数,用除留余数法构造哈希函数
return m; //并返回模值
}
int Hash2(NA str){//哈希函数
long n;
int m;
n = atoi(str);//把字符串转换成整型数.
m=n%HASHSIZE; //用除留余数法构造哈希函数
return m; //并返回模值
}
Status collision(int p,int &c){ // 冲突处理函数,采用二次探测再散列法解决冲突
int i,q;
i=c/2+1;
while( i < HASHSIZE ){
if(c%2==0){
c++;
q=(p+i*i)%HASHSIZE;
if(q>=0) return q;
else i=c/2+1;
}
else{
q=(p-i*i)%HASHSIZE;
c++;
if(q>=0) return q;
else i=c/2+1;
}
}
return UNSUCCESS;
}
int searchHash( HashTable * &H, NA key, int &p, int &c ,int way )
{
if( way == 1 ){
p=Hash1( key );
while(H->elem[p]!=NULL && !eq( key, H->elem[p]->name ) )//若哈希地址冲突,进行冲突处理
collision( p ,++c );
if( eq( key, H->elem[p]->name ) )
return 1;
else
return 0;
}
else
{
p=Hash2( key );
while(H->elem[p]!=NULL && !eq( key, H->elem[p]->tel ) )//若哈希地址冲突,进行冲突处理
collision( p ,++c );
if( eq( key, H->elem[p]->tel ) )
return 1;
else
return 0;
}
}
// 建表,若哈希地址冲突,进行冲突处理
void CreateHash(HashTable* H ,Record* a)
{
cout<<"\n 〓〓〓〓〓〓建立散列表〓〓〓〓〓〓〓";
cout<<"\n ⑴. 以姓名建立散列表(再散列法解决冲突) ";
cout<<"\n ⑵. 以电话号码建立散列表(再散列法解决冲突) ";
cout<<"\n ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆";
cout<<"请输入选择:";
int num,i,p=-1,c;
cin>>num;
for(i=0;i<NUM_BER;i++){
c=0;
if( num==1 ){
p=Hash1( a[i].name );
while(H->elem[p]!=NULL && !eq( a[i].name, H->elem[p]->name ) )//若哈希地址冲突,进行冲突处理
collision( p ,++c );
}
else{
p=Hash2( a[i].tel );
while(H->elem[p]!=NULL && !eq( a[i].tel, H->elem[p]->tel ) )//若哈希地址冲突,进行冲突处理
collision( p ,++c );
}
H->elem[p]=a+i; //求得哈希地址,将信息存入
H->count++;
cout<<"第"<<i+1<<"个记录冲突次数为"<<c<<"。

\n";//需要显示冲突次数时输出
}
cout<<"\n建表完成!\n此哈希表容量为"<<HASHSIZE<<",当前表内存储的记录个数为"<<H->count<<".\n";
}
// 查找用户名和电话号码的记录;
void SearchHash ( HashTable* H,int &c){//在通讯录里查找关键字,若查找成功,显示信息
//c用来记录冲突次数,查找成功时显示冲突次数
NA type;
int p;
cout<<"\n 〓〓〓〓〓〓查找并显示用户信息记录〓〓〓〓〓〓〓";
cout<<"\n ⑴. 查找并显示给定用户名的记录";
cout<<"\n ⑵. 查找并显示给定电话号码的记录";
cout<<"\n ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆";
cout<<"请输入选择:";
int num;
cin>>num;
switch(num){
case 1:
cout<<"\n请输入要查找的用户名:\n";
cin>>type;
searchHash( H, type, p, c, 1);
if( eq( type, H->elem[p]->name )==1){
cout<<"\n查找成功!以下是您需要要查找的信息:\n\n";
cout<<"姓名:"<<H->elem[p]->name<<"\n电话号码:"<<H->elem[p]->tel<<"\n 联系地址:"<<H->elem[p]->add<<"\n";
}
else
cout<<"\n对不起,该用户不存在\n";
break;
case 2:
cout<<"\n请输入要查找电话号码:\n";
cin>>type;
searchHash( H, type, p, c, 2);
if( eq( type, H->elem[p]->tel )==1){
cout<<"\n查找成功!以下是您需要要查找的信息:\n\n";
cout<<"姓名:"<<H->elem[p]->name<<"\n电话号码:"<<H->elem[p]->tel<<"\n 联系地址:"<<H->elem[p]->add<<"\n";
}
else
cout<<"\n对不起,该用户不存在\n";
break;
default:
cout<<"输入错误,请重新输入!";
}
}
void Save(){ //保存
ifstream in;
ofstream out;
out.open("123.txt");
printf("\n保存成功!");
for(int i=0; i<NUM_BER; i++ ){
out<<"姓名:"<<a[i].name<<"\n电话号码:"<<a[i].tel<<"\n联系地址:"<<a[i].add<<"\n";
}
return;
}
void main_menu()
{
int c,flag=1;////定义一个布尔型变量flag并初始化为真(true)
HashTable *H;
H=(HashTable*)malloc(LEN);
for(int i=0;i<HASHSIZE;i++)
H->elem[i]=NULL;
H->size=HASHSIZE;
H->count=0;
while (1){ // while使电话查询系统执行后返回主菜单的界面
system("cls");
cout<<"\n 〓〓〓〓〓〓↖(^ω^)↗欢迎使用电话号码查找系统〓〓〓〓〓〓〓";
cout<<"\n ⑴. 添加用户信息";
cout<<"\n ⑵. 读取所有用户信息";
cout<<"\n ⑶. 建立散列表(再散列法解决冲突) ";
cout<<"\n ⑷. 查找并显示给定用户的记录";
cout<<"\n ⑸. 保存";
cout<<"\n ⑹. 退出";
cout<<"\n 提示:进行4操作前请先进行3操作 .否则无法查找成功!"; cout<<"\n ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆";
cout<<"\n\n请选择:";
int num;
cin>>num;
switch(num){
case 1:
getin(a);
system("pause");
break;
case 2:
ShowInformation(a);
system("pause");
break;
case 3:
CreateHash (H,a); /* 建立散列表*/
system("pause");
break;
case 4:
c=0;
SearchHash (H,c);
system("pause");
break;
case 5:
Save();
system("pause");
break;
case 6:
return ;
break;
default:
cout<<"输入错误,请重新输入!";
cout<<"\n";
}
}
}
int main(int argc, char* argv[])
{
main_menu();
//执行结束后清屏,显示主菜单
return 0;
} [调试分析]
问题一:
现象:每次执行后,系统界面不自动清屏?
原因:执行每个选项后,没有清屏操作的代码。

解决办法:将CLS放到switch函数体后面,执行选项后,执行清屏操作
问题二:
现象:进行查找的时候,总是查找失败! ! !
原因:查找前没有建立散列表。

解决办法:先建立散列表,再查找。

[运行结果及分析]
课程设计题目二
[实验内容]
通讯录
[问题描述]
管理通讯信息,包含姓名(name )、出生日期(birthday)、城市(city)、职业(profession)、电话(tel)、QQ号(qq)等。

[需求分析]
1、输入信息。

2、显示信息。

3、分别以姓名、城市、出生日期、电话或QQ作为关键字查找。

4、删除信息。

5、存盘,将数据保存在文件phonefile中,以便下次可以继续上述工作。

测试数据:自己设计,不少于30人。

[概要设计]
void TeleNumber::ReadFile(istream & in) //从文件依次将标准输入流中的数据读入到变量name,.... 中
void TeleNumber::input() //信息数据输入
void TeleNumber::display() //调用telenumber中的display函数输出信息
void TeleMessage::Remove() //删除
void TeleMessage::Show()
int main()
[存储结构]
此程序的存储结构是单链表
void TeleMessage::Insert() //插入
{ End->input(); //尾指针指向input,从单链表尾部插入信息
End->Next=new TeleNumber; //尾节点指向下一个数据节点,开辟一个新的节点
End=End->Next; //尾节点指向下一个节点
cout<<endl<<"插入成功"<<endl;
[流程图]
[详细设计]
#include<iostream > //对标准输入输出文件的操作,后面有命名空间,不能用<iostream.h>,否则出错
#include<fstream> //是对文件操作
#include<string>
#include<stdlib.h>
using namespace std;
int num ;
struct TeleNumber //用struct结构体来存放不同类型的数据,函数
{
char name[20]; //姓名,字符必须注明长度
int phoneNumber; //电话号码
int birthday; //出生日期
char city[30]; //城市
TeleNumber * Next;
void ReadFile(istream & in);//
void input(); //信息输入的函数
void display();//信息输出的函数
} ;
void TeleNumber::ReadFile(istream & in) //从文件依次将标准输入流中的数据读入到变量name,.... 中
{ in>>name>>phoneNumber>>birthday>>city;
}
void TeleNumber::input() //信息数据输入
{ cout<<"请输入姓名"<<endl;
cin>>name;
cout<<"请输入电话号码"<<endl;
cin>>phoneNumber;
cout<<"请输入出生日期"<<endl;
cin>>birthday;
cout<<"请输入城市"<<endl;
cin>>city;
}
void TeleNumber::display() //调用telenumber中的display函数输出信息
{ cout<<"姓名:"<<name<<'\t'<<"固定号码:"<<phoneNumber<<'\t'
<<"出生日期:"<<birthday<<'\t'<<"城市:"<<city<<endl;
}
class TeleMessage //建造一个类,表示信息功能的类{ public:
TeleMessage(); //构造数据结构
~TeleMessage(); //析构函数,用来释放单链表
void Save(); //数据保存到文件
TeleNumber * Search(char *); //定义一个查找数据的指针
void Insert(); //插入函数的定义在后面,这里要先声明
void Remove(); //删除
void Change(); //更改
void Show(); //显示
private:
TeleNumber * End,* Head; //定义结构体的头尾指针
ifstream in; //定义从文件中读入数据
ofstream out; //定义将数据写到文件中
}; TeleMessage::TeleMessage() //定义信息类中的信息函数
{ Head=new TeleNumber; //头插法建立单链表Head->Next=new TeleNumber; //head指向一个新分配的节点
End=Head->Next; //对结构体遍历,头节点指针依次指向下一个节点,直到指向尾节点
in.open("TeleNumber.text"); //打开外存文件,读入数据
} //使屏幕暂停,而不是直接闪过
TeleMessage::~TeleMessage() //释放单链表
{ TeleNumber * temp; //定义一个数据指针temp
while(Head->Next!=End) //当头节点指向的下一个指针不是尾节点
{ temp=Head->Next; //数据指针指向头节点指向的下一个节点Head=Head->Next; //头结点依次指向头结点的下一个节点
delete temp; //删除数据指针指向的节点数据
}
delete Head,End; //删除头尾指针
}
void TeleMessage::Save() //保存文件
{ out.open("TeleNumber.txt"); //建立外存文件TeleNumber.txt,把数据写入外存文件中
for(TeleNumber *p=Head->Next;p!=End;p=p->Next) out<<p->name<<"\t"<<p->phoneNumber<<"\t"<<p->birthday<<"\t"<<p->city<<endl;//将数据存到外存文件里
out.close(); //定义一个数据指针p,使p依次指向头结点指向的下一个指针,直到p指向尾节点,依次写入每个节点的信息
cout<<"保存成功!"<<endl;
}
void TeleMessage::Insert() //插入
{ End->input(); //尾指针指向input,从单链表尾部插入信息
End->Next=new TeleNumber; //尾节点指向下一个数据节点,开辟一个新的节点
End=End->Next; //尾节点指向下一个节点
cout<<endl<<"插入成功"<<endl;
}
void TeleMessage::Remove() //删除
{ char name[20];
TeleNumber * p=new TeleNumber,*temp=NULL; //定义一个数据指针,指向新的数据,定义一个数据指针temp,指向空函数
cout<<"请输入要删除人的姓名:"<<endl;
cin>>name;
if( (p=Search(name))) //如果找到了要删除的节点
{ if( Head->Next==p){
Head->Next=p->Next;//利用头指针,将要删除的数据拉出来,这个是经过找很多人写出来的。

首先非常感谢百度知友和校友
}
temp=p; //使指针temp指向p指向的要删除的节点
p=p->Next; //摘链:使p指向p指向的下一个节点的下一个节点
delete temp; //删除temp指向的节点
cout<<"删除成功!"<<endl;
}
else
{ cout<<"没有找到!"<<endl;
}
}
TeleNumber * TeleMessage::Search(char * name) //定义信息类中查找函数指针
{ for(TeleNumber *p=Head->Next;p!=End;p=p->Next) //定义一个数据指针p指向头节点指向的下一个节点,头节点没有数据,数据从头节点的下一个节点开始存储,P进行遍历操作,直到指向尾节点
if(!strcmp(p->name,name)) //strcmp作用是俩个变量相减,如果p所指向的name 和要查找的name相同,则strcmp值为0,非1
{ p->display();
return p;
}
else
cout<<"查无此人"<<endl;
return 0;
system("cls");
}
void TeleMessage::Show()
{ for(TeleNumber * p=Head->Next;p!=End;p=p->Next)
p->display();
}
int main()
{ bool flag=true;//定义一个布尔型变量flag并初始化为真(true),只认识真(true)和假(false),1和0,也就是成立和不成立的简单判断。

TeleMessage tele;
char name[20];
while(flag) //如果flag为真
{
system("cls");
cout<<"〓〓〓〓〓〓〓通讯录〓〓〓〓〓〓〓"<<endl;
cout<<" 1.增加信息"<<endl;
cout<<" 2.显示信息"<<endl;
cout<<" 3.查找号码"<<endl;
cout<<" 4.删除信息"<<endl;
cout<<" 5.保存信息"<<endl;
cout<<" 6.退出系统"<<endl;
cout<<"――――――――――――――――― "<<endl;
cout<<"请选择:";
cin>>num;
switch(num)
{ case 1:tele.Insert();break;
case 2:tele.Show();break;
case 3: cout<<"请输入姓名"<<endl;
cin>>name;
tele.Search(name);break;
case 4:tele.Remove();break;
case 5:tele.Save();break;
case 6: cout<<"谢谢使用! "<<endl;return 0;break; //return 0表示退出系统
default:cout<<"输入错误,请重新输入!";
cout<<"\n";
}
system("pause ");
} }
[调试分析]
问题一:
现象:执行查找时,运行出错。

原因:定义查找时,出错了,经过调试if语句,运行成功
问题二:
现象:按0,系统不退出
原因:输入0后,系统执行的是break,而不是return 0.
问题三:
现象:删除操作时,系统显示俩排相同的信息
原因:定义删除函数时,一个for,一个if导致删除执行了俩次。

解决办法是for消掉[运行结果及分析]
三、参考文献:
[1].《C程序设计》第二版,谭浩强著。

北京:清华大学出版社,2004年。

[2].《数据结构》严蔚敏、吴伟民。

北京:清华大学出版社,2005年。

相关文档
最新文档