数据结构实验报告七查找、

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

云南大学软件学院数据结构实验报告
(本实验项目方案受“教育部人才培养模式创新实验区(X3108005)”项目资助)实验难度: A □ B □ C □
学期:2010秋季学期
任课教师:
实验题目: 查找算法设计与实现
姓名: 王辉
学号: 20091120154
电子邮件:
完成提交时间: 2010 年 12 月 27 日
云南大学软件学院2010学年秋季学期
《数据结构实验》成绩考核表
学号:姓名:本人承担角色:
综合得分:(满分100分)
指导教师:年月日(注:此表在难度为C时使用,每个成员一份。


(下面的内容由学生填写,格式统一为,字体: 楷体, 行距: 固定行距18,字号: 小四,个人报告按下面每一项的百分比打分。

难度A满分70分,难度B满分90分)一、【实验构思(Conceive)】(10%)
1 哈希表查找。

根据全年级学生的姓名,构造一个哈希表,选择适当的哈希函数和解决冲突的方法,设计并实现插入、删除和查找算法。

熟悉各种查找算法的思想。

2、掌握查找的实现过程。

3、学会在不同情况下运用不同结构和算法求解问题。

4 把每个学生的信息放在结构体中:
typedef struct //记录
{
NA name;
NA tel;
NA add;
}Record;
5 void getin(Record* a)函数依次输入学生信息
6 人名折叠处理,先将用户名进行折叠处理折叠处理后的数,用除留余数法构造哈希函数,并返回模值。

并采用二次探测再散列法解决冲突。

7姓名以汉语拼音形式,待填入哈希表的人名约30个,自行设计哈希函数,用线性探测再散列法或链地址法处理冲突;在查找的过程中给出比较的次数。

完成按姓名查询的操作。

将初始班级的通讯录信息存入文件。

二、【实验设计(Design)】(20%)
(本部分应包括:抽象数据类型的功能规格说明、主程序模块、各子程序模块的伪码说明,主程序模块与各子程序模块间的调用关系)
1抽象数据类型的功能规格说明和结构体:
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include <windows.h>
#define MAXSIZE 20 //电话薄记录数量
#define MAX_SIZE 20 //人名的最大长度
#define HASHSIZE 53 //定义表长
#define SUCCESS 1
#define UNSUCCESS -1
#define LEN sizeof(HashTable)
typedef int Status;
typedef char NA[MAX_SIZE];
typedef struct //记录
{
NA name;
NA tel;
NA add;
}Record;
typedef struct //哈希表
{
Record *elem[HASHSIZE]; //数据元素存储基址
int count; //当前数据元素个数
int size; //当前容量
}HashTable;
2 主函数与各子函数的调用关系:(通过switch(num)函数按不同功能要求分别调用相关函数)int main(int argc, char* argv[]){
system("color 61");
int c,flag=1;
HashTable *H;
H=(HashTable*)malloc(LEN);
for(int i=0;i<HASHSIZE;i++)
H->elem[i]=NULL;
H->size=HASHSIZE;
H->count=0;
Record a[MAXSIZE];
while (1)
{
printf("\n ★☆★☆★☆★☆★☆wang hui★☆★☆★☆★☆★☆★☆");
printf("\n ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
printf("\n ★☆★☆我的未来不是梦★☆★☆");
printf("\n ★☆★☆无聊中郁闷死★☆★☆");
printf("\n ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
printf("\n ┏━━━━━━━━━━━━━┓");
printf("\n ★┃欢迎欢迎欢迎欢迎欢迎欢迎┃★");
printf("\n 〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒"); printf("\n ★★★★★★★哈希表的设计与实现★★★★★★");
printf("\n 【】. 添加用户信息");
printf("\n 【】. 读取所有用户信息");
printf("\n 【】. 以姓名建立哈希表(再哈希法解决冲突) ");
printf("\n 【】. 以电话号码建立哈希表(再哈希法解决冲突) ");
printf("\n 【】. 查找并显示给定用户名的记录");
printf("\n 【】. 查找并显示给定电话号码的记录");
printf("\n 【】. 清屏");
printf("\n 【】. 保存");
printf("\n 【】. 退出程序");
printf("\n 温馨提示:");
printf("\n Ⅰ.进行操作前请先输出");
printf("\n Ⅱ.进行操作前请先输出");
printf("\n ★★★★★★★〒〒〒〒〒〒〒〒〒★★★★★★");
printf("\n");
printf("请输入一个任务选项>>>");
printf("\n");
int num;
scanf("%d",&num);
switch(num){
case 1:
getin(a);
break;
case 2:
ShowInformation(a);
break;
case 3:
CreateHash1(H,a); /* 以姓名建立哈希表*/
break;
case 4:
CreateHash2(H,a); /* 以电话号码建立哈希表*/
break;
case 5:
c=0;
SearchHash1(H,c);
break;
case 6:
c=0;
SearchHash2(H,c);
break;
case 7:
Cls(a);
break;
case 8:
Save();
break;
case 9:
return 0;
break;
default:
printf("你输错了,请重新输入!");
printf("\n");
}
}
system("pause");
return 0;
}
三、【实现描述(Implement)】(30%)
(本部分应包括:抽象数据类型具体实现的函数原型说明、关键操作实现的伪码算法、函数设计、函数间的调用关系,关键的程序流程图等,给出关键算法的时间复杂度分析。


1 主函数打印和主子函数调用:
int main(int argc, char* argv[]){
system("color 61");
int c,flag=1;
HashTable *H;
H=(HashTable*)malloc(LEN);
for(int i=0;i<HASHSIZE;i++)
H->elem[i]=NULL;
H->size=HASHSIZE;
H->count=0;
Record a[MAXSIZE];
while (1)
{
printf("\n ★☆★☆★☆★☆★☆申平★☆★☆★☆★☆★☆★☆");
printf("\n ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
printf("\n ★☆★☆我的未来不是梦★☆★☆");
printf("\n ★☆★☆无聊中郁闷死★☆★☆");
printf("\n ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
printf("\n ┏━━━━━━━━━━━━━┓");
printf("\n ★┃欢迎欢迎欢迎欢迎欢迎欢迎┃★");
printf("\n 〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒〒");
printf("\n ★★★★★★★哈希表的设计与实现★★★★★★");
printf("\n 【】. 添加用户信息");
printf("\n 【】. 读取所有用户信息");
printf("\n 【】. 以姓名建立哈希表(再哈希法解决冲突) ");
printf("\n 【】. 以电话号码建立哈希表(再哈希法解决冲突) ");
printf("\n 【】. 查找并显示给定用户名的记录");
printf("\n 【】. 查找并显示给定电话号码的记录");
printf("\n 【】. 清屏");
printf("\n 【】. 保存");
printf("\n 【】. 退出程序");
printf("\n 温馨提示:");
printf("\n Ⅰ.进行操作前请先输出");
printf("\n Ⅱ.进行操作前请先输出");
printf("\n ★★★★★★★〒〒〒〒〒〒〒〒〒★★★★★★");
printf("\n");
printf("请输入一个任务选项>>>");
printf("\n");
int num;
scanf("%d",&num);
switch(num){
case 1:
getin(a);
break;
case 2:
ShowInformation(a);
break;
case 3:
CreateHash1(H,a); /* 以姓名建立哈希表*/
break;
case 4:
CreateHash2(H,a); /* 以电话号码建立哈希表*/
break;
case 5:
c=0;
SearchHash1(H,c);
break;
case 6:
c=0;
SearchHash2(H,c);
break;
case 7:
Cls(a);
break;
case 8:
Save();
break;
case 9:
return 0;
break;
default:
printf("你输错了,请重新输入!");
printf("\n");
}
}
system("pause");
return 0;
}
2键盘输入各人的信息:
void getin(Record* a) //键盘输入各人的信息
{
system("cls");
printf("输入要添加的个数:\n");
scanf("%d",&NUM_BER);
int i;
for(i=0;i<NUM_BER;i++){
printf("请输入第%d个记录的用户名:\n",i+1);
scanf("%s",a[i].name);
printf("请输入%d个记录的电话号码:\n",i+1);
scanf("%s",a[i].tel);
printf("请输入第%d个记录的地址:\n",i+1);
scanf("%s",a[i].add); //gets(str2);??????
}
}
3显示输入的用户信息和人名的折叠处理:
void ShowInformation(Record* a) //显示输入的用户信息
{
system("cls");
int i;
for( i=0;i<NUM_BER;i++)
printf("\n第%d个用户信息:\n 姓名:%s\n 电话号码:%s\n 联系地址:%s\n",i+1,a[i].name,a[i].tel,a[i].add);
}
void Cls(Record* a){
printf("*");
system("cls");
}
long fold(NA s) //人名的折叠处理
{
char *p;
long sum=0;
NA ss;
strcpy(ss,s); //复制字符串,不改变原字符串的大小写
strupr(ss); //将字符串ss转换为大写形式
p=ss;
while(*p!='\0')
sum+=*p++;
printf("\nsum====================%d",sum);
return sum;
}
5哈希函数, 先将用户名进行折叠处理, 折叠处理后的数,用除留余数法构造哈希函数并返回模值; 把字符串转换成整型数,用除留余数法构造哈希函数并返回模值:
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; //并返回模值
}
6冲突处理函数,采用二次探测再散列法解决冲突:
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;
}
void benGetTime();
void CreateHash1(HashTable* H,Record* a) //建表,以人的姓名为关键字,建立相应的散列表
{
system("cls"); //若哈希地址冲突,进行冲突处理
benGetTime();
int i,p=-1,c,pp;
for(i=0;i<NUM_BER;i++){
c=0;
p=Hash1(a[i].name);
pp=p;
while(H->elem[pp]!=NULL) {
pp=collision(p,c);
if(pp<0){
printf("第%d记录无法解决冲突",i+1); //需要显示冲突次数时输出continue;
} //无法解决冲突,跳入下一循环
}
H->elem[pp]=&(a[i]); //求得哈希地址,将信息存入
H->count++;
printf("第%d个记录冲突次数为%d。

\n",i+1,c); //需要显示冲突次数时输出
}
printf("\n建表完成!\n此哈希表容量为%d,当前表内存储的记录个数为%d.\n",HASHSIZE,H->count);
benGetTime();
}
7在通讯录里查找姓名关键字,若查找成功,显示信息:
void SearchHash1(HashTable* H,int &c) //在通讯录里查找姓名关键字,若查找成功,显示信息
{
system("cls"); //c用来记录冲突次数,查找成功时显示冲突次数
benGetTime();
NA str;
printf("\n请输入要查找记录的姓名:\n");
scanf("%s",str);
int p,pp;
p=Hash1(str);
pp=p;
while((H->elem[pp]!=NULL)&&(eq(str,H->elem[pp]->name)==-1))
pp=collision(p,c);
if(H->elem[pp]!=NULL&&eq(str,H->elem[pp]->name)==1){
printf("\n查找成功!\n查找过程冲突次数为%d.以下是您需要要查找的信息:\n\n",c);
printf("姓名:%s\n电话号码:%s\n联系地址:%s\n",H->elem[pp]->name,H->elem[pp]->tel,H->elem[pp]->add);
}
else printf("\n此人不存在,查找不成功!\n");
benGetTime();
}
8建表,以电话号码为关键字,建立相应的散列表, 若哈希地址冲突,进行冲突处理: void CreateHash2(HashTable* H,Record* a) //建表,以电话号码为关键字,建立相应的散列表
{
//若哈希地址冲突,进行冲突处理
benGetTime();
int i,p=-1,c,pp;
for(i=0;i<NUM_BER;i++){
c=0;
p=Hash2(a[i].tel);
pp=p;
while(H->elem[pp]!=NULL) {
pp=collision(p,c);
if(pp<0){
printf("第%d记录无法解决冲突",i+1); //需要显示冲突次数时输出continue;
} //无法解决冲突,跳入下一循环
}
H->elem[pp]=&(a[i]); //求得哈希地址,将信息存入
H->count++;
printf("第%d个记录冲突次数为%d。

\n",i+1,c); //需要显示冲突次数时输出}
printf("\n建表完成!\n此哈希表容量为%d,当前表内存储的记录个数为%d.\n",HASHSIZE,H->count);
benGetTime();
}
9 在通讯录里查找电话号码关键字,若查找成功,显示信:
void SearchHash2(HashTable* H,int &c) //在通讯录里查找电话号码关键字,若查找成功,显示信息
{
system("cls"); //c用来记录冲突次数,查找成功时显示冲突次数
benGetTime();
NA tele;
printf("\n请输入要查找记录的电话号码:\n");
scanf("%s",tele);
int p,pp;
p=Hash2(tele);
pp=p;
while((H->elem[pp]!=NULL)&&(eq(tele,H->elem[pp]->tel)==-1))
pp=collision(p,c);
if(H->elem[pp]!=NULL&&eq(tele,H->elem[pp]->tel)==1){
printf("\n查找成功!\n查找过程冲突次数为%d.以下是您需要要查找的信息:\n\n",c);
printf("姓名:%s\n电话号码:%s\n联系地址:%s\n",H->elem[pp]->name,H->elem[pp]->tel,H->elem[pp]->add);
}
else printf("\n此人不存在,查找不成功!\n");
benGetTime();
}
void Save(){
FILE *fp;
if((fp=fopen("c:\test.txt", "w"))==NULL){
printf("\nERROR opening customet file");
}
fclose(fp);
}
四、【测试结果(Testing)】(10%)
(本部分应包括:对实验的测试结果,应具体列出每次测试所输入的数据以及输出的数据,并对测试结果进行分析总结)
五、【实验总结】(10%)
(本部分应包括:自己在实验中完成的任务,注意组内的任意一位同学都必须独立完成至少一项接口的实现;对所完成实验的经验总结、心得)
1 通常有两类方法处理冲突:开放定址(Open Addressing)法和拉链(Chaining)法。

前者是将所有结点均存放在散列表T[0..m-1]中;后者通常是将互为同义词的结点链成一个单链表,而将此链表的头指针放在散列表T[0..m-1]中。

2 用开放定址法解决冲突的做法是:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。

沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。

查找时探查到开放的地址则表明表中无待查的关键字,即查找失败。

注意:
①用开放定址法建立散列表时,建表前须将表中所有单元(更严格地说,是指单元中存储的关键字)置空。

②空单元的表示与具体的应用相关。

3 拉链法解决冲突的方法
拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。

若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。

凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。

T中各分量的初值均应为空指针。

在拉链法中,装填因子α可以大于1,但一般均取α≤1.
4 拉链法的优点
与开放定址法相比,拉链法有如下几个优点:
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
(3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。

而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
(4)在用拉链法构造的散列表中,删除结点的操作易于实现。

只要简单地删去链表上相应的结点即可。

而对开放地址法构造的散列表,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。

这是因为各种开放地址法中,空地址单元(即开放地址)都是查找失败的条件。

因此在用开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。

5 拉链法的缺点
拉链法的缺点是:指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。

2 查找记录时需进行查找值和关键字的比较。

这一类查找方法建立在“比较”的基础上。

一般的查找方法有:顺序查找、折半查找、二叉查找。

查找的效率依赖于查找过程中所进行的比较次数,甚至有一些数据的位置会影响某一次查找效率。

理想的是希望不经任何比较,一次存取便能得到所需查找的记录。

这个理想的情况可以通过哈希表来实现。

六、【代码】(10%)
(本部分应包括:完整的代码及充分的注释。

注意纸质的实验报告无需包括此部分。

格式统一为,字体: Georgia , 行距: 固定行距12,字号: 小五)。

相关文档
最新文档