哈希表的作用是用来实现高效的查找,相比较数组、链表等更加高效。通过关键字和值的一一对应来实现。和C++的关联容器有点像,但是map内部实现是通过红黑树来实现的。相对于而二叉查找树,散列表只支持其中的一部分操作。散列是一种用以常数平均时间执行插入、删除、和查找的技术。
哈希表的实现主要涉及两个方面,一是哈希函数的确定,二是解决经过哈希函数之后存在的冲突问题。
(1)哈希函数
哈希函数也叫散列函数,它的作用是将关键字映射到0到tablesize-1的范围中的某个数,并且放到合适的单元,理想情况下这个函数应该运算简单并且保证任何两个不同的关键字映射到不同的单元,但这几乎是不可能的,表的大小有限,但是关键字的数量是无限的。
typedef unsigned int Inedx;
Index Hash(const char*Key,nt TableSize)
{
Index HashVal=0;
while(*Key!='\0')
HashVal=(HashVal<<5)+*Key++;
return HashVal%TableSize;
}
(2)解决冲突
这里讨论两种方法,分离链接法和开放定址法。
1、分离链接法
做法是将散列到同一个值得所有元素保留到一个表中。名字难理解但是其实说简单一点就是将Hash之后得到相同结果的值存放在一个链表中。这样构成一个散列表,它包括一个链表数组以及数组中链表的个数。具体实现如下:
#include "Hash.h"
//hash函数
Index Hash(const char*Key,nt TableSize)
{
Index HashVal=0;
while(*Key!='\0')
HashVal=(HashVal<<5)+*Key++;
return HashVal%TableSize;
}
//初始化一个散列表
HashTable InitializeTable( int TableSize)
{
HashTable H;
int i;
if(TableSize<MinTableSize)
{
printf("TableSize size too small");
return 0;
}
//创建hash表
H=malloc(sizeof(struct HashTbl));
if(H==NULL)
printf("out of space!");
//设置表的大小为素数
H->TableSize=NextPrime(TableSize);
//创建一个链表数组,数组每个元素是一个链表,数组大小是hash表的大小
H->TheList=malloc(sizeof(List)*H->TableSize);
if(H->TheList==NULL)
printf("out of space!");
//创建链表
for(i=0;i<TableSize;i++)
{
H->TheList[i]=malloc(sizeof(struct ListNode));
if(H->TheList[i]==NULL)
printf("out of space");
else
H->TheList[i]->Next=NULL;
}
return 0;
}
//如果Key是字符串的话,需要用字符串比较函数
Position Find( ElementType key, HashTable H)
{
Position P;
List L;
//hash函数返回一个值,L为指向这值的指针。一个值可能对应多个,L等于说是一个链表的头节点。
L=H->TheList[(Hash(key,H))];
P=L->next;
while(P!=NULL&&P->Element!=key)
P=P->next;
return P;
}
//有点不好的地方是计算了两次hash函数。
void Insert( ElementType Key, HashTable H)
{
Position P,Tmp;
List L;
P=find(Key,H);
if(P==NULL)
{
Tmp=malloc(sizeof(ListNode));
if(Tmp==NULL)
printf("out of space!!");
else
{
L=H->TheList[hash(Key,H)];
Tmp->next=L->next;
Tmp->Element=Key;
L->next=Tmp;
}
}
}
(2)开放定址法
开放定址包括线性探测法、平方探测法、双散列。其实还是一个表只是在遇到冲突的时候解决冲突的几种办法。
线性探测法:探测函数为F(i)=i,步骤是先计算哈希值,然后看这个位置是否为空,若为空则存放于此,若不为空则在下一个位置。
平方探测法:探测函数为F(i)=i^2,步骤是计算哈希值,然后看这个位置是否为空,若为空则存放于此,若不为空则使用i^2作为步长,作为下一个位置,若超过表长则可以减去表长。