题目描述:为班级30个人的姓名设计一个哈希表,假设姓名用汉语拼音表示。要求用除留余数法
构造哈希函数,用线性探测再散列法处理冲突,平均查找长度的上限为2。编写数据结构和算法来实现。并在此基础上通过适当修改,采用顺序查找和二分查找对姓名进行查找,计算两种方法的查找长度。
本次有文档操作,故直接放本次文件
百度网盘提取提取码:2t3x
你的三连就是我创作的最大动力!
顺序查找的平均长度:
n
+
1
2
\frac{n+1}{2}
2n+1
折半查找的平均长度 :
log
2
(
n
+
1
)
−
1
(
n
⟶
∞
)
\log_2(n+1)-1 (n\longrightarrow\infty )
log2(n+1)−1(n⟶∞) 当n大于50时候,接近于
log
2
(
n
+
1
)
−
1
\log_2(n+1)-1
log2(n+1)−1
用不同方法解决冲突时哈希表的平均查找长度(成功)
解决冲突的办法 | 成功的查找(a为装填因子) |
---|---|
线性探查法 | 1 2 ( 1 + 1 1 − a ) \frac{1}{2}(1+\frac{1}{1-a} ) 21(1+1−a1) |
平方探查法 | − 1 a log e ( 1 − a ) -\frac{1}{a} \log_e(1-a) −a1loge(1−a) |
拉链法 | 1 + a 2 1+\frac{a}{2} 1+2a |
本例中,装填因子取0.71,平均查找长度约等于2,符合题意。
解答:
1.顺序查找算法的实现
/*顺序查找法*/
int SeqSearch(RecordList l,string cname){
l.a[0].name=cname;
int i=l.length;
while(l.a[i].name!=cname) i--;
return i;
}
2.折半查找算法的实现
/*先进行排序,折半查找法*/
int BinSearch(RecordList l,string cname){
int low=1,high=l.length;
int mid;
while(low<=high){
mid=(low+high)/2;
if(l.a[mid].name==cname) return mid;
else if(l.a[mid].name>cname) high=mid-1;
else low=mid+1;
}
return 0;
}
3.散列查找算法的实现
/*在hash表中查找*/
void hashFind(string cname){
int csum=asc(cname);
int adr=csum%M,d=adr,count=1;
if(hasha[adr].name==cname) cout<<cname<<"在hash表中是第"<<d<<"位,"<<"查找了"<<count<<"几次"<<endl;
else if(hasha[adr].sum==0) cout<<"查无此人"<<endl;
else{
while(1){
d=(d+(csum%10)+1)%M;
count++;
if(hasha[d].name==cname){
cout<<cname<<"在hash表中是第"<<d<<"位,"<<"查找了"<<count<<"几次"<<endl;
break;
}
if(hasha[d].sum==0){
cout<<"查无此人"<<endl;
break;
}
}
}
}
将上述进行整合,其中30个姓名已做成txt文档。
并且通过Excel文档对三种方法进行整合,下面附完整代码进行验证。其中需要说明的是,这里折半查找的排序是通过字符串大小进行排序的,而Hash则是通过字符串的ASCII码除留余数法进行创建。其中hash装填因子我选的是0.71,(一共30个数据,hash表的长度为41)
有时间更新哈希表各种方法与STL中的hash,这个还是比较重要的!
下面附完整代码:
#include <iostream>
#include <cstring>
#include <string>
#include <fstream>
#include <algorithm>
#include <cmath>
using namespace std;
const int hashlength=41;
#define M 37//比表长最大的素数
typedef struct{
string name;
int sum;
}Stud;
typedef struct{
Stud a[100];//a[0]为工作单元
int length;
}RecordList;
typedef struct{
string name;
int sum;
int si;
}HashList;
/*以ASCII将姓名从小到大排*/
bool cmp(Stud a,Stud b){
return a.name<b.name;
}
/*ascii求和*/
int asc(string name){
int sum=0;
for(int i=0;i<name.length();i++){
sum+=name[i]-'a';
}
return sum;
}
/*顺序查找法*/
int SeqSearch(RecordList l,string cname){
l.a[0].name=cname;
int i=l.length;
while(l.a[i].name!=cname) i--;
return i;
}
/*先进行排序,折半查找法*/
int BinSearch(RecordList l,string cname){
int low=1,high=l.length;
int mid;
while(low<=high){
mid=(low+high)/2;
if(l.a[mid].name==cname) return mid;
else if(l.a[mid].name>cname) high=mid-1;
else low=mid+1;
}
return 0;
}
HashList hasha[hashlength];
/*创建hash表*/
void CreatHash(RecordList l){
int i,count,adr,d;
/*初始化hash列表*/
for(i=1;i<hashlength;i++){
hasha[i].name="0";
hasha[i].sum=0;
hasha[i].si=0;
}
for(i=1;i<=30;i++){
count=1;
adr=(l.a[i].sum)%M;//hash函数
d=adr;
if(hasha[adr].si==0){//如果不冲突
hasha[adr].name=l.a[i].name;
hasha[adr].sum=l.a[i].sum;
hasha[adr].si=1;
}
else{
while(1){
d=(d+(l.a[i].sum%10)+1)%M;
count++;
if(hasha[d].sum==0) break;
}
hasha[d].name=l.a[i].name;
hasha[d].sum=l.a[i].sum;
hasha[d].si=count;
}
}
}
/*在hash表中查找*/
void hashFind(string cname){
int csum=asc(cname);
int adr=csum%M,d=adr,count=1;
if(hasha[adr].name==cname) cout<<cname<<"在hash表中是第"<<d<<"位,"<<"查找了"<<count<<"次"<<endl;
else if(hasha[adr].sum==0) cout<<"查无此人"<<endl;
else{
while(1){
d=(d+(csum%10)+1)%M;
count++;
if(hasha[d].name==cname){
cout<<cname<<"在hash表中是第"<<d<<"位,"<<"查找了"<<count<<"次"<<endl;
break;
}
if(hasha[d].sum==0){
cout<<"查无此人"<<endl;
break;
}
}
}
}
int main(){
RecordList l;
l.length=0;
ifstream in("Name.txt", ios::in);
if (!in.is_open()) {
cout <<"文件打开错误!"<< endl;
exit (0);
}
cout<<"姓名保存在Name.txt文档中"<<endl;
int i=1;
while (!in.eof()) {
in >> l.a[i].name;
l.length++;
i++;
}
for(i=1;i<=30;i++){
l.a[i].sum=asc(l.a[i].name);
}
cout<<"请输入需要查找的姓名拼音"<<endl;
string s;
cin>>s;
//顺序查找表
int t=SeqSearch(l,s);
if(t!=0){
cout<<s<<"出现在乱序文件的第"<<t<<"位"<<endl;
}
else{
cout<<"查无此人"<<endl;
}
cout<<"顺序查找成功的平均查找长度为"<<0.5*(30+1)<<endl;
//折半查找
sort(l.a+1,l.a+31,cmp);
int tm=BinSearch(l,s);
if(tm!=0){
cout<<s<<"出现在正序文件的第"<<tm<<"位"<<endl;
}
else{
cout<<"查无此人"<<endl;
}
cout<<"折半查找成功的平均长度"<<log(31)/log(2)-1<<endl;
//Hash表
CreatHash(l);
hashFind(s);
return 0;
}
下面是运行结果
我这里查找的是lihao字符,通过excel表格查看,确实符合上述答案。当然我提前在excel表格中创建这序列,通过打印字符串然后保存文件。