哈希表存储的基本思想是:
以数据表中的每个记录的关键字 k为自变量,通过一种函数H(k)计算出函数值。把这个值解释为一块连续存储空间(即数组空间)的单元地址(即下标),将该记录存储到这个单元中。在此称该函数H为哈希函数或散列函数。按这种方法建立的表称为哈希表或散列表。
主要解决的问题:
在理想的情况下,通过哈希函数可以建立键和值的一一对应关系,但是由于关键字值的某种随机性,使得这种一一对应关系难以发现或构造。因而可能会出现不同的关键字对应一个存储地址。即k1≠k2,但H(k1)=H(k2),这种现象称为冲突。有了冲突后我们就要去解决。
对于哈希技术,主要研究两个问题:
(1)如何设计哈希函数以使冲突尽可能少地发生。
(2)发生冲突后如何解决。
(1)哈希函数的的设计:
1.直接定址法
直接定址法是以关键字k本身或关键字加上某个数值常量c作为哈希地址的方法。该哈希函数H(k)为:
H(k)=k+c (c≥0) 这种哈希函数计算简单,并且不可能有冲突发生。当关键字的分布基本连续时,可使用直接定址法的哈希函数(这是这种方法使用的条件)。否则,若关键字分布不连续将造成内存单元的大量浪费。
2.除留余数法
取关键字k除以哈希表长度m所得余数作为哈希函数地址的方法。即:
H(k)=k%m 这是一种较简单、也是较常见的构造方法。这种方法的关键是选择好哈希表的长度m。使得数据集合中的每一个关键字通过该函数转化后映射到哈希表的任意地址上的概率相等。理论研究表明,在m取值为素数(质数)时,冲突可能性相对较少。
3.平方取中法
取关键字平方后的中间几位作为哈希函数地址(若超出范围时,可再取模)。
4.折叠法
这种方法适合在关键字的位数较多,而地址区间较小的情况。
将关键字分隔成位数相同的几部分。然后将这几部分的叠加和作为哈希地址(若超出范围,可再取模)。
5.数值分析法
若事先知道所有可能的关键字的取值时,可通过对这些关键字进行分析,发现其变化规律,构造出相应的哈希函数。
(2)解决冲突的方法:
一.开放定址法
用开放定址法处理冲突就是当冲突发生时,形成一个地址序列。沿着这个序列逐个探测,直到找出一个“空”的开放地址。将发生冲突的关键字值存放到该地址中去。
如 Hi=(H(k)+d(i)) % m, i=1,2,…k (k<m-1)
其中H(k)为哈希函数,m为哈希表长,d为增量函数,d(i)=dl,d2…dn-l。
增量序列的取法不同,可得到不同的开放地址处理冲突探测方法。
2)线性探测法
线性探测法是从发生冲突的地址(设为d)开始,依次探查d+l,d+2,…m-1(当达到表尾m-1时,又从0开始探查)等地址,直到找到一个空闲位置来存放冲突处的关键字。若整个地址都找遍仍无空地址,则产生溢出。
线性探查法的数学递推描述公式为:
d0=H(k)
di=(di-1+1)% m (1≤i≤m-1)
利用线性探查法处理冲突容易造成关键字的“堆积”问题。这是因为当连续n个单元被占用后,再散列到这些单元上的关键字和直接散列到后面一个空闲单元上的关键字都要占用这个空闲单元,致使该空闲单元很容易被占用,从而发生非同义冲突。造成平均查找长度的增加。
(3)平方探查法
设发生冲突的地址为d,则平方探查法的探查序列为:d+12,d+22,…直到找到一个空闲位置为止。
平方探查法的数学描述公式为:
d0=H(k)
di=(d0+i2)% m (1≤i≤m-1)
二.链地址法
用链地址法解决冲突的方法是:把所有关键字为同义词的记录存储在一个线性链表中,这个链表称为同义词链表。并将这些链表的表头指针放在数组中(下标从0到m-1)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_SIZE1 20
#define MAX_SIZE2 50
typedef struct word{
char word_name[MAX_SIZE1];
char word_mean[MAX_SIZE2];
struct word *next;
}WORD; //哈希表中要存的东西
//从文件中读取数据 来创建节点
int make_word_node(FILE *fp,WORD **node)
{
*node=(WORD *)malloc(sizeof(WORD));//创建一个节点
memset(*node,0,sizeof(WORD));//初始化文件的大小
fscanf(fp,"%s %s",(*node)->word_name,(*node)->word_mean);
if(1==feof(fp)) return 1;
(*node)->next=NULL; //创建了一个结点
return 0;
}
void build_haxi_table(WORD **arry,int haxisize)
{
int ret=-1;
WORD *new_node;
int haxiIndex=0;
int acssvalue=0;
int i;
FILE *fp=fopen("file","r");
while(1)
{
//创建新的结点
ret=make_word_node(fp,&new_node);
if(1==ret) break ;
//在创建了新的结点后我们要把这个结点 按照哈希函数插入到表中
//哈希函数
for(i=0;i<strlen(new_node->word_name);i++)
acssvalue+=new_node->word_name[i];
haxiIndex=acssvalue%haxisize;//找到了要插入的位置
if(NULL==arry[haxiIndex]) arry[haxiIndex]=new_node;
else //就要处理冲突
{
WORD *p=arry[haxiIndex];
while(p->next)
{
p=p->next;
}
p->next=new_node;
}
}
}
void search_word(WORD **arry,WORD **out,char *word_name,int haxisize)
{
int i;
int haxiIndex=0;
int acssvalue=0;
for(i=0;i<strlen(word_name);i++)
acssvalue+=word_name[i];
haxiIndex=acssvalue%haxisize;//找到了下标了
if(NULL==arry[haxiIndex])
{
return ;
}
else if(NULL==arry[haxiIndex]->next)
{
*out=arry[haxiIndex];
}
else
{
WORD* p=arry[haxiIndex];
while(1)
{
if(NULL==p||0==strcmp(p->word_name,word_name)) break;
p=p->next;
}
if(NULL==p) printf("not find\n");
*out=p;
}
}
void printallword(WORD **arry,int size)
{
int i=0;
for(i=0;i<size;i++)
{
WORD *p=arry[i];
while(1)
{
if(p==NULL) break;
printf("the word name is%s the word meaingis %s\n",p->word_name,p->word_mean);
p=p->next;
}
}
}
int main()
{
int haxisize=20;
int i=0;
WORD *array[haxisize];
WORD *out=(WORD *)malloc(sizeof(WORD));
for(i=0;i<haxisize;i++){
array[i]=NULL;
}
build_haxi_table(array,haxisize);
printallword(array,haxisize);
search_word(array,&out,"jy",haxisize);
printf("the word name is%s the word meaingis %s\n",out->word_name,out->word_mean);
return 0;
}