一、Hash哈希表
无论是栈、队列、还是顺序表等线性结构中,记录的关键字和存储位置之间都不存在一 一对应的关系。在查找记录的时候,则需要对关键字进行逐一比对,过程繁琐,查找效率低。 哈希表是一种高效的查找结构,它的出现很好地解决了这个问题,接下来让我们逐步深入学习。
二、哈希函数
哈希函数是哈希表的灵魂,正因为它的存在,哈希表才完成了从记录关键字到存储位置的一一映射关系。类比y=f(x),存储位置作为因变量是由自变量关键字经过某种函数关系计算而得的。
哈希函数的构造方法:
- 直接定址法(y=x):不作变换,直接使用关键字作为哈希地址。
- 除留余数法(y=x%a,其中a为常数)
- 数字分析法:在关键字中选择分布均匀的位数作为哈希地址(这种方法观察主观选择,与函数无关)
- 折叠法
- 平方取中法
三、冲突以及处理冲突的方法
首先什么叫冲突? 我们都知道想y=x^2,当y=1时方程有两个解1或者-1。可是如果“1”代表的是唯一的存储地址1,那么这时候究竟是存放“1”还是“-1”呢?(冲突,也就这样产生了) 想要解决冲突就是要解决两个解如何存放的问题(一山不能容二虎)。
平时操场集合的时候,前面排列着各个班的班牌(有且只有一个),每个班都不止一位同学,如何大家都站在班牌那,这就发生了冲突,所以该如何站呢? 很简单,以班牌为地址,同班同学在同一列,逐一向后排列即可。
同样的道理,如果我们能在存储地址1的下方放置另一个解“-1”,依此类推,那冲突的问题就解决了。
因此,我们需要创建一个指针数组,指针数组里的每个元素就好像是操场上的班牌一样,指向着整个班,而后,每个记录都采用链表结点的方式,在同一“班牌”下的记录,按先来后到的方式链接在后面。 如图:
- 开放地址法:
既然哈希函数可以实现记录关键字和存储位置的一一对应,那我们直接可以新建一个处理冲突的函数(y=x^2-1或者其他可以改变y的函数),这样子,实现了多个解发生冲突的时候,可以错开存放。 另外, 根据所选的冲突函数不同,又分为线性探测法和二次探测法(y=x^(2) -a ^(2))
四、哈希表的实现
根据不同的处理冲突的方法,哈希表可以分成两类链地址哈希表和开放定址哈希表 (本次主要介绍链地址哈希表,下一讲再介绍开放定址哈希表)
链地址哈希表类型定义:
typedef struct {
KeyType key;
}RcdType;
typedef struct Node {
RcdType r;
struct Node *next;
}Node;
typedef struct {
Node **rcd;
int size;
int count;
int (*hash)(KeyType key,int size);
}Hash;
链地址哈初始化:
Status InitHash (Hash &H,int size,int (*hash)(int,int)) {
H.rcd = (Node**)malloc(size*sizeof(Node*));
if (NULL==H.rcd) {
return ERROR;
}
else {
for (int i=0;i<size;i++) H.rcd[i] =NULL;
H.size = size;
H.count = ERROR;
H.hash = hash;
return ERROR;
}
}
查找记录:
Node * SearchHash(Hash &H,int key) {
int h = hash(key,H.size);
Node *p ;
for (p = H.rcd[h];p!=NULL;p = p->next) {
if (p->r.key==key) return p;
}
return NULL;
}
在表头插入记录:
Status InsertHash (Hash &H,RcdType e) {
if (SearchHash(H,e.key)==NULL) {
int h = hash(e.key,H.size);
Node *p ;
p->r = e;
p->next = H.rcd[h];
H.rcd[h] = p;
H.count++;
return OK;
}
else {
return FALSE;
}
}
删除记录:
Status DeleteHash (Hash &H,int key,RcdType &e) {
Node *p = SearchHash(H,e.key);
if (p==NULL) {
return 0;
}
else {
key = p->r.key;
int h = hash(e.key,H.size);
Node *pr;
while (pr->next!=p) {
pr = pr->next;
}
pr = p->next;
free(p);
H.count--;
return key;
}
}
|