哈希表设计—紧急求救,高分相送

zx_cv 2002-06-18 06:49:43
问题:针对某个集体(比如你所在的班级)中的“人名”设计一个哈希表,使得平均
查找长度不超过R,完成相应的建表和查表程序。
要求:假设人名是中国人姓名的汉语拼音形式。待填入哈希表的人名共有30个,取平均查
找长度的上限为2。哈希函数用除留余数法构造,用伪随机探测再散例法处理冲突。
...全文
1007 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
zx_cv 2002-06-19
  • 打赏
  • 举报
回复
老大的些,我要的是实习报告的完整算法,不好意思,我不能给你们,我们要考试了,才来求救的,希望有 人能写出来,要是谁写出来,我送100分!
hammer_shi 2002-06-18
  • 打赏
  • 举报
回复
散列表上的运算

 散列表上的运算有查找、插入和删除。其中主要是查找,这是因为散列表的目的主要是用于快速查找,且插入和删除均要用到查找操作。

1、散列表类型说明:
#define NIL -1 //空结点标记依赖于关键字类型,本节假定关键字均为非负整数
#define M 997 //表长度依赖于应用,但一般应根据。确定m为一素数
typedef struct{ //散列表结点类型
KeyType key;
InfoType otherinfo; //此类依赖于应用
}NodeType;
typedef NodeType HashTable[m]; //散列表类型

2、基于开放地址法的查找算法
 散列表的查找过程和建表过程相似。假设给定的值为K,根据建表时设定的散列函数h,计算出散列地址h(K),若表中该地址单元为空,则查找失败;否则将该地址中的结点与给定值K比较。若相等则查找成功,否则按建表时设定的处理冲突的方法找下一个地址。如此反复下去,直到某个地址单元为空(查找失败)或者关键字比较相等(查找成功)为止。

(1)开放地址法一般形式的函数表示
int Hash(KeyType k,int i)
{ //求在散列表T[0..m-1]中第i次探查的散列地址hi,0≤i≤m-1
//下面的h是散列函数。Increment是求增量序列的函数,它依赖于解决冲突的方法
return(h(K)+Increment(i))%m; //Increment(i)相当于是di
}
若散列函数用除余法构造,并假设使用线性探查的开放定址法处理冲突,则上述函数中的h(K)和Increment(i)可定义为:
int h(KeyType K){ //用除余法求K的散列地址
return K%m;
}

int Increment(int i){//用线性探查法求第i个增量di
return i; //若用二次探查法,则返回i*i
}
(2)通用的开放定址法的散列表查找算法:
int HashSearch(HashTable T,KeyType K,int *pos)
{ //在散列表T[0..m-1]中查找K,成功时返回1。失败有两种情况:找到一个开放地址
//时返回0,表满未找到时返回-1。 *pos记录找到K或找到空结点时表中的位置
int i=0; //记录探查次数
do{
*pos=Hash(K,i); //求探查地址hi
if(T[*pos].key==K) return l; //查找成功返回
if(T[*pos].key==NIL) return 0;//查找到空结点返回
}while(++i<m) //最多做m次探查
return -1; //表满且未找到时,查找失败
} //HashSearch

注意:
 上述算法适用于任何开放定址法,只要给出函数Hash中的散列函数h(K)和增量函数Increment(i)即可。但要提高查找效率时,可将确定的散列函数和求增量的方法直接写入算法HashSearch中,相应的算法【参见习题】。

3、基于开放地址法的插入及建表
 建表时首先要将表中各结点的关键字清空,使其地址为开放的;然后调用插入算法将给定的关键字序列依次插入表中。
 插入算法首先调用查找算法,若在表中找到待插入的关键字或表已满,则插入失败;若在表中找到一个开放地址,则将待插入的结点插入其中,即插入成功。
void Hashlnsert(HashTable T,NodeTypene w)
{ //将新结点new插入散列表T[0..m-1]中
int pos,sign;
sign=HashSearch(T,new.key,&pos); //在表T中查找new的插入位置
if(!sign) //找到一个开放的地址pos
T[pos]=new; //插入新结点new,插入成功
else //插人失败
if(sign>0)
printf("duplicate key!"); //重复的关键字
else //sign<0
Error("hashtableoverflow!"); //表满错误,终止程序执行
} //Hashlnsert

void CreateHashTable(HashTable T,NodeType A[],int n)
{ //根据A[0..n-1]中结点建立散列表T[0..m-1]
int i
if(n>m) //用开放定址法处理冲突时,装填因子α须不大于1
Error("Load factor>1");
for(i=0;i<m;i++)
T[i].key=NIL; //将各关键字清空,使地址i为开放地址
for(i=0;i<n;i++) //依次将A[0..n-1]插入到散列表T[0..m-1]中
Hashlnsert(T,A[i]);
} //CreateHashTable

4、删除
 基于开放定址法的散列表不宜执行散列表的删除操作。若必须在散列表中删除结点,则不能将被删结点的关键字置为NIL,而应该将其置为特定的标记DELETED。
 因此须对查找操作做相应的修改,使之探查到此标记时继续探查下去。同时也要修改插人操作,使其探查到DELETED标记时,将相应的表单元视为一个空单元,将新结点插入其中。这样做无疑增加了时间开销,并且查找时间不再依赖于装填因子。
 因此,当必须对散列表做删除结点的操作时,一般是用拉链法来解决冲突。
注意:
 用拉链法处理冲突时的有关散列表上的算法









/*************************************/
/*1、MY TOOL:*************************/
/*2、Delphi,Oracle,Sybase,C++/C*******/
/*3、PROJECT:*************************/
/*3、Boss*****************************/
/*4、为了五星的目标希望你早点结贴*********/
/*5、在线技术支持(QQ):68123095*********/
/*************************************/

hammer_shi 2002-06-18
  • 打赏
  • 举报
回复
散列函数的构造方法

1、散列函数的选择有两条标准:简单和均匀。
 简单指散列函数的计算简单快速;
 均匀指对于关键字集合中的任一关键字,散列函数能以等概率将其映射到表空间的任何一个位置上。也就是说,散列函数能将子集K随机均匀地分布在表的地址集{0,1,…,m-1}上,以使冲突最小化。

2、常用散列函数
 为简单起见,假定关键字是定义在自然数集合上。

(1)平方取中法
 具体方法:先通过求关键字的平方值扩大相近数的差别,然后根据表长度取中间的几位数作为散列函数值。又因为一个乘积的中间几位数和乘数的每一位都相关,所以由此产生的散列地址较为均匀。
 【例】将一组关键字(0100,0110,1010,1001,0111)平方后得
(0010000,0012100,1020100,1002001,0012321)
 若取表长为1000,则可取中间的三位数作为散列地址集:
(100,121,201,020,123)。
相应的散列函数用C实现很简单:
int Hash(int key){ //假设key是4位整数
key*=key; key/=100; //先求平方值,后去掉末尾的两位数
return key%1000; //取中间三位数作为散列地址返回
}

(2)除余法
 该方法是最为简单常用的一种方法。它是以表长m来除关键字,取其余数作为散列地址,即 h(key)=key%m
 该方法的关键是选取m。选取的m应使得散列函数值尽可能与关键字的各位相关。m最好为素数。
 【例】若选m是关键字的基数的幂次,则就等于是选择关键字的最后若干位数字作为地址,而与高位无关。于是高位不同而低位相同的关键字均互为同义词。
 【例】若关键字是十进制整数,其基为10,则当m=100时,159,259,359,…,等均互为同义词。

(3)相乘取整法
 该方法包括两个步骤:首先用关键字key乘上某个常数A(0<A<1),并抽取出key.A的小数部分;然后用m乘以该小数后取整。即:

 该方法最大的优点是选取m不再像除余法那样关键。比如,完全可选择它是2的整数次幂。虽然该方法对任何A的值都适用,但对某些值效果会更好。Knuth建议选取

 该函数的C代码为:
int Hash(int key){
double d=key *A; //不妨设A和m已有定义
return (int)(m*(d-(int)d));//(int)表示强制转换后面的表达式为整数
}

(4)随机数法
 选择一个随机函数,取关键字的随机函数值为它的散列地址,即
h(key)=random(key)
 其中random为伪随机函数,但要保证函数值是在0到m-1之间。








/*************************************/
/*1、MY TOOL:*************************/
/*2、Delphi,Oracle,Sybase,C++/C*******/
/*3、PROJECT:*************************/
/*3、Boss*****************************/
/*4、为了五星的目标希望你早点结贴*********/
/*5、在线技术支持(QQ):68123095*********/
/*************************************/

hammer_shi 2002-06-18
  • 打赏
  • 举报
回复
不知道是你没说清还是我理解不好,不太明白你的目的!
整理一份资料给你参考:
 散列方法不同于顺序查找、二分查找、二叉排序树及B-树上的查找。它不以关键字的比较为基本操作,采用直接寻址技术。在理想情况下,无须任何比较就可以找到待查关键字,查找的期望时间为O(1)。

散列表的概念

1、散列表
 设所有可能出现的关键字集合记为U(简称全集)。实际发生(即实际存储)的关键字集合记为K(|K|比|U|小得多)。
 散列方法是使用函数h将U映射到表T[0..m-1]的下标上(m=O(|U|))。这样以U中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在O(1)时间内就可完成查找。
其中:
  ① h:U→{0,1,2,…,m-1} ,通常称h为散列函数(Hash Function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|U|个值减少到m个值,从而降低空间开销。
 ② T为散列表(Hash Table)。
 ③ h(Ki)(Ki∈U)是关键字为Ki结点存储地址(亦称散列值或散列地址)。
 ④ 将结点按其关键字的散列地址存储到散列表中的过程称为散列(Hashing)


3、散列表的冲突现象
(1)冲突
 两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。该现象称为冲突(Collision)或碰撞。发生冲突的两个关键字称为该散列函数的同义词(Synonym)。
 【例】上图中的k2≠k5,但h(k2)=h(k5),故k2和K5所在的结点的存储地址相同。

(2)安全避免冲突的条件
 最理想的解决冲突的方法是安全避免冲突。要做到这一点必须满足两个条件:
①其一是|U|≤m
②其二是选择合适的散列函数。
  这只适用于|U|较小,且关键字均事先已知的情况,此时经过精心设计散列函数h有可能完全避免冲突。

(3)冲突不可能完全避免
 通常情况下,h是一个压缩映像。虽然|K|≤m,但|U|>m,故无论怎样设计h,也不可能完全避免冲突。因此,只能在设计h时尽可能使冲突最少。同时还需要确定解决冲突的方法,使发生冲突的同义词能够存储到表中。

(4)影响冲突的因素
 冲突的频繁程度除了与h相关外,还与表的填满程度相关。
 设m和n分别表示表长和表中填人的结点数,则将α=n/m定义为散列表的装填因子(Load Factor)。α越大,表越满,冲突的机会也越大。通常取α≤1。




/*************************************/
/*1、MY TOOL:*************************/
/*2、Delphi,Oracle,Sybase,C++/C*******/
/*3、PROJECT:*************************/
/*3、Boss*****************************/
/*4、为了五星的目标希望你早点结贴*********/
/*5、在线技术支持(QQ):68123095*********/
/*************************************/

33,009

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧