散列表查询
- 散列表技术是在记录的存储位置和它关键字之间建立一个确定的关系F,使得每个关键字KEY对应一个存储位置F(KEY)
- 这里把这种对应关系F称为散列散列函数,又称为哈希函数
- 采用散列技术将记录存储在一块连续的存储空间中,这块存储空间称为散列表或哈希表。
散列表的查找步骤:
- 当存储记录时,通过散列函数计算出记录的散列地址
- 当查找记录时,同样通过散列函数计算记录的散列地址,并按此散列地址访问该记录
- 散列表其实就是用值投影一个地址;直接可通过值查找到记录
构造散列函数的两个基本原则:
计算简单;分布均匀
方法:
- 直接定址法:可取关键字某个线性函数的值为散列地址。即 F(key)=a*key+b;(表中数据小且连续)
- 数分析法:适合处理关键字位数比较大的情况;关键字位数较大,通过数字分析,找出表中每个数据的不可能重复位作为散列地址(一般为关键字中用来为数据编号的位置)
- 平方取中法:将关键字平方之后取中间若干位数字作为散列地址(适用于不知道关键字分布,且关键字位数不大的情况)
- 折叠法:将关键字从左到右分割成位数相等的几个部分,然后将这几部分叠加求和,并按散列表表长取后几位作为散列地址(适用于不知道关键字分布,但知道关键字位数)
- 除留余数法:对散列表长m的散列函数计算公式:F(key)=key%p(p<=m);p的选择狠关键:最好【接近/等于】m或m的最大质数
- 随机数法:去关键字的随机数值为他的散列地址。即:F(key)=random(key);(适用于关键字长度不等的情况)
选取方法时可参照的方向:
- 计算散列地址所用的时间
- 关键字长度
- 散列表大小
- 关键字分布情况
- 记录查找的评率
处理散列冲突的方法:
- 开放定址法:一旦发生了冲突,就去寻找下一个空的散列表地址【Fi(key)=(F(key)+Di)%m(Di=1,2,3,...,m-1)】
- 再散列函数法:一旦发生了冲突,RHi表示多个散列函数,冲突之后就换一个散列函数重新定址【Fi(key)=RHi(key) (i=1,2,3,...,k)】
- 链地址法:发生冲突时,利用单链表把数据链在原数的指针上
- 公共溢出区法:把冲突数据存放在溢出表的同样位置
1 #define HASESIZE 12 2 #define NULLKEY -32768 3 4 //利用散列函数得到偏移地址,在数组中对偏移地址存储相应数据达到查找效率为O(1) 5 typedef struct{ 6 int *elem;//数据元素基址(首地址),动态分配数组; 7 int count;//当前数据元素个数 8 }HashTable; 9 10 int InitHashTable(HashTable *H){ 11 int i; 12 H->count=HASESIZE; 13 H->elem=(int *)malloc(HASESIZE*sizeof(int)); 14 if(!H->elem){ 15 return -1; 16 } 17 for(i=0;i<HASESIZE;i++){ 18 H->elem[i]=NULLKEY; 19 } 20 return 0; 21 } 22 23 //使用除留余数法 24 int Hash(int key){ 25 return key % HASESIZE; 26 } 27 28 //插入关键字到散列表 29 void InsertHash(HashTable *H,int key){ 30 int addr=Hash(key); 31 while(H->elem[addr]!=NULLKEY){//出现冲突 32 addr=(addr+1) % HASESIZE;//开放定址法处理 33 if(addr==Hash(key)){ 34 return ;//已近存满 35 } 36 } 37 H->elem[addr]=key; 38 } 39 40 //散列表查找关键字 41 int SearchHase(HashTable H,int key,int *addr){ 42 *addr=Hash(key); 43 while(H.elem[*addr]!=key){ 44 *addr=(*addr+1)%HASESIZE; 45 if(H.elem[*addr]==NULLKEY || *addr==Hash(key)){ 46 return -1; 47 } 48 } 49 return 0; 50 }
浙公网安备 33010602011771号