hash表定义:hashing定义了一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。
设所有可能出现的关键字集合记为u(简称全集)。实际发生(即实际存储)的关键字集合记为k(|k|比|u|小得多)。|k|是集合k中元素的个数。
散列方法是使用函数hash将u映射到表t[0..m-1]的下标上(m=o(|u|))。这样以u中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在o(1)时间内就可完成查找。
其中:
① hash:u→{0,1,2,…,m-1} ,通常称h为散列函数(hash function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|u|个值减少到m个值,从而降低空间开销。
② t为散列表(hash table)。
③ hash(ki)(ki∈u)是关键字为ki结点存储地址(亦称散列值或散列地址)。
④ 将结点按其关键字的散列地址存储到散列表中的过程称为散列(hashing).
比如:有一组数据包括用户名字、电话、住址等,为了快速的检索,我们可以利用名字作为关键码,hash规则就是把名字中每一个字的拼音的第一个字母拿出来,把该字母在26个字母中的顺序值取出来加在一块作为该记录的地址。比如张三,就是z+s=26+19=45。就是把张三存在地址为45处。
但是这样存在一个问题,比如假如有个用户名字叫做:周四,那么计算它的地址时也是z+s=45,这样它与张三就有相同的地址,这就是冲突,也叫作碰撞(hash碰撞)。
冲突:两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。该现象称为冲突(collision)或碰撞。发生冲突的两个关键字称为该散列函数的同义词(synonym)。
冲突基本上不可避免的,除非数据很少,我们只能采取措施尽量避免冲突,或者寻找解决冲突的办法。影响冲突的因素
冲突的频繁程度除了与h相关外,还与表的填满程度相关。
设m和n分别表示表长和表中填入的结点数,则将α=n/m定义为散列表的装填因子(load factor)。α越大,表越满,冲突的机会也越大。通常取α≤1。
简单实现程序如下:
其中hash_i()为hash函数,提供两种处理冲突的方法线性探测法和双重散列法。用的测试数据为100000以内的不重复随机数,
装填因子α=9000/11000,也就是9000个数放到11000个盒子里;
测试结果:数据较少时,双重散列法偏优,α=9000/11000时两种处理冲突方法得到的平均查找长度基本相当。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <hash_map.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
#define ARRAY_SIZE 9000
#define HASH_LENGTH 11000
int hash_i(int i) {
return (3*i)%HASH_LENGTH;
}
//template<class T>
int main(int argc, char* argv[]) {
//int ai[]={22,41,53,46,30,13,01,67,32};
//constractor the test random array ai[]
srand((int)time(NULL));
int ai[ARRAY_SIZE]={0};
int i=0;
int *hashtable = (int*)malloc(sizeof(int)*(HASH_LENGTH+1));
for(i = 0; i < ARRAY_SIZE; i++) {
int t = rand()%100000;
int k;
for(k = 0; k < ARRAY_SIZE; k++) {
if(t == ai[k])
break;
}
if(k != ARRAY_SIZE) continue;
ai[i]=t;
}
//initial hashtable[]
for(i=0; i< HASH_LENGTH; i++) {
hashtable[i]=-1;
}
int k=1;
int m = 1;
for(i = 0;i < ARRAY_SIZE ;i++ ) {
int n;
printf("hash value%d=%d\n",i,n=hash_i(ai[i]));
if( hashtable[n] == -1 ) {
hashtable[n]=ai[i];
}
else {
int tmp1,tmp2;
#ifndef DOUBLE_HASHING
//线性探测法
//探测序列从不成功hash值后面逐个加1比较,即n,n+1,...HASH_LENGTH-1,0,1,..n-1
do {
tmp2 = (n+m)%HASH_LENGTH;
if(hashtable[tmp2] == -1) {
hashtable[tmp2] = ai[i];
printf("tmp2=%d,try again total %d times\n",tmp2,k++);
break;
}
}while( tmp2 != n ,m++);
#else
//双重散列法(double hashing)
//h(key)为hash(key)
//h1=(h(key)+i*i)%m
//hi=(h(key)+i*h1(key))%m
//m为hash表长,hi为第i次的散列值
for(m = 0; m < HASH_LENGTH ;m++) {
tmp1 = (n+m*m)%HASH_LENGTH;
tmp2 = (n+m*tmp1)%HASH_LENGTH;
if(hashtable[tmp2] == -1) {
hashtable[tmp2]=ai[i];
printf("tmp2=%d,try again total %d times\n",tmp2,k++);
break;
}
}
#endif
if(m == HASH_LENGTH)
printf("table is full!\n");
}
}
for(i = 0; i < HASH_LENGTH ;i++)
printf("hashtable[%d]=%d\n",i,hashtable[i]);
printf("不成功的asl=%.2f\n",(k-1)*1.0/ARRAY_SIZE);
if(hashtable) {
free(hashtable);
hashtable = NULL;
}
return 0;
}