哈希概念
理想的搜素方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。
如果构造一种一种存储结构,通过某种函数使元素的存储位置与他的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:
a
- 插入元素时,根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。
- 搜索元素时,对元素的关键码进行同样的计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相同,则搜索成功。
该方式称为 哈希方法,哈希方法中使用的转换函数称为哈希函数,构造出来的结构称为哈希表。
哈希冲突
哈希冲突:
对于两个数据元素的关键字Ki和Kj(i!=j),有Ki!=Kj,但有 :
HashFun(Ki)==HashFun(Kj)
即不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
哈希函数
引起哈希冲突的一个原因可能是:哈希函数设计的不够合理:
- 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0-m-1之间
- 哈希函数计算出来的地址能均匀分布在整个空间中
- 哈希函数应该比较简单
处理哈希冲突
常见的解决哈希冲突的两种解决方法是:闭散列和开散列
闭散列
闭散列:当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放列表中下一个空位中。
任何寻找下一个空位:
可以采用线性探测:从发生冲突的位置开始,依次继续向后探测,直到找到空位为止。
使用哈希函数找到待插入元素在哈希表中的位置,如果该位置中没有元素则直接插入新元素;如果该位置中有元素且和待插入元素相同,则不用插入;如果该位置中有元素但不是待插入元素则发生哈希冲突,使用线性探测找到下一个空位,插入新元素。
负载因子
哈希表的负载因子定义为:α=填入表中的元素个数/散列表的长度
α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,表明填入表中的元素越多。产生冲突的可能性就越大;反之,α越小,标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是载荷因子α的函数,只是不同处理冲突的方法有不同的函数。
对于闭散列,载荷因子是特别重要因素,应严格限制在0.7-0.8以下。超过0.8,查表时的CPU缓存不命中按照指数曲线上升。
闭散列
开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头节点存储在哈希表中。
实现闭散列
typedef int HashType;
#define MaxSize 1000
typedef int Keytype;
typedef int Valtype;
typedef enum Stat{
Empty,
Valid,
Deleted,
} Stat;
typedef size_t (*HashFun)(Keytype);
typedef struct HashElem{
Keytype key;
Valtype value;
Stat stat;
}HashElem;
typedef struct HashTable{
HashElem data[MaxSize];
size_t size;
HashFun func;
}HashTable;
size_t HashDefault(Keytype key)
{
return key % MaxSize;
}
void HashInit(HashTable *ht)
{
if(ht==NULL)
{
return;
}
ht->size=0;
ht->func=HashDefault;
size_t i=0;
for(i=0;i<MaxSize;i++)
{
ht->data[i].stat=Empty;
}
}
void HashDestroy(HashTable *ht)
{
if(ht==NULL)
{
return;
}
ht->func=NULL;
ht->size=0;
size_t i=0;
for(i=0;i<MaxSize;i++)
{
ht->data[i].stat=Empty;
}
}
void HashInsert(HashTable *ht,Keytype key,Valtype value)
{
if(ht==NULL)
{
return;
}
//规定负载因子:0.8
if(ht->size>=0.8*MaxSize)
{
return;
}
size_t offset=ht->func(key);
while(1)
{
//可插入位置可以插入
if(ht->data[offset].stat!=Valid)
{
ht->data[offset].key=key;
ht->data[offset].value=value;
ht->data[offset].stat=Valid;
ht->size++;
return;
}
//存在重复元素,插入失败
else if(ht->data[offset].key==key&&ht->data[offset].stat==Valid)
{
return;
}
else
{
offset++;
if(offset>=MaxSize)
{
offset=0;
}
}
}
}
int HashFind(HashTable *ht,Keytype key,Valtype* value)
{
if(ht==NULL)
{
return 0;
}
if(ht->size==0)
{
return 0;
}
size_t offset=ht->func(key);
while(1)
{
//查找成功
if(ht->data[offset].key==key&&ht->data[offset].stat==Valid)
{
*value=ht->data[offset].value;
return 1;
}
//查找失败
else if(ht->data[offset].stat==Empty)
{
return 0;
}
else
{
offset++;
if(offset>=MaxSize)
{
offset=0;
}
}
}
return 0;
}
void HashRemove(HashTable *ht,Keytype key)
{
if(ht==NULL)
{
return;
}
if(ht->size==0)
{
return;
}
size_t offset=ht->func(key);
while(1)
{
if(ht->data[offset].key==key&&ht->data[offset].stat==Valid)
{
ht->data[offset].stat=Empty;
--ht->size;
}
else if(ht->data[offset].stat==Empty)
{
return;
}
else
{
offset++;
if(offset>=MaxSize)
{
offset=0;
}
}
}
}
实现开散列
typedef int HashType;
#define MaxSize 1000
typedef int Keytype;
typedef int Valtype;
typedef size_t (*HashFun)(Keytype);
typedef struct HashElem{
Keytype key;
Valtype value;
struct HashElem* next;
}HashElem;
typedef struct HashTable{
HashElem* data[MaxSize];
size_t size;
HashFun func;
}HashTable;
size_t HashDefault(Keytype key)
{
return key%MaxSize;
}
void HashInit(HashTable *ht)
{
if(ht==NULL)
{
return;
}
ht->size=0;
ht->func=HashDefault;
size_t i=0;
for(i=0;i<MaxSize;i++)
{
ht->data[i]=NULL;
}
}
void DestroyNode(HashElem* cur)
{
free(cur);
cur=NULL;
}
void HashDestroy(HashTable *ht)
{
if(ht==NULL)
{
return;
}
if(ht->size==0)
{
return;
}
ht->size=0;
ht->func=NULL;
size_t i=0;
for(i=0;i<MaxSize;i++)
{
HashElem* cur=ht->data[i];
if(cur==NULL)
{
continue;
}
while(cur!=NULL)
{
HashElem* pre=cur;
DestroyNode(cur);
cur=pre->next;
}
}
}
HashElem* CreatNode(Keytype key,Valtype value)
{
HashElem* new_node=(HashElem* )malloc(sizeof(HashElem));
new_node->key=key;
new_node->value=value;
new_node->next=NULL;
return new_node;
}
void HashInsert(HashTable *ht,Keytype key,Valtype value)
{
if(ht==NULL)
{
return;
}
if(ht->size>=MaxSize)
{
return;
}
size_t offset=ht->func(key);
HashElem* cur=ht->data[offset];
while(1)
{
while(cur!=NULL)
{
if(cur->key==key)
{
return;
}
cur=cur->next;
}
HashElem* new_elem=CreatNode(key,value);
new_elem->next=ht->data[offset];
ht->data[offset]=new_elem;
break;
}
ht->size++;
}
int HashFind(HashTable *ht,Keytype key,Valtype* value)
{
if(ht==NULL)
{
return 0;
}
if(ht->size==0)
{
return 0;
}
size_t offset=ht->func(key);
HashElem* cur=ht->data[offset];
while(cur!=NULL)
{
if(cur->key==key)
{
*value=cur->value;
return 1;
}
cur=cur->next;
}
return 0;
}
void HashRemove(HashTable *ht,Keytype key)
{
if(ht==NULL)
{
return;
}
if(ht->size==0)
{
return;
}
size_t offset=ht->func(key);
HashElem* cur=ht->data[offset];
HashElem* pre=NULL;
while(cur!=NULL)
{
if(cur->key==key)
{
break;
}
pre=cur;
cur=cur->next;
}
if(cur==NULL)
{
return;
}
if(pre==NULL)
{
HashElem* tmp=ht->data[offset];
ht->data[offset]=tmp->next;
DestroyNode(tmp);
}
else
{
pre->next=cur->next;
DestroyNode(cur);
}
}