一、hash算法简介
Hash算法网上也有很多资料,本次大致讲解汇总一下。
Hash即散列,散列方法是根据入参取其中的关键字key作为变量,通过函数公式h(K)(称为散列函数)得到一个函数值,这个函数值一般就是数组的下标,相应的输入信息就存入改下标的内存处,检索时,用同样的方法计算地址,然后到相应的单元里去取要找的结点。一般使用hash算法就是为了快速查找,用内存来换时间,一般在性能提升的时候,大多数当for循环太多的时候,查找特别耗性能,就建议使用hash算法替代for循环,在工作中能胜任大部分的性能提升。
引用别人的博客一段一段话:(https://www.jianshu.com/p/f9239c9377c5)
按散列存储方式构造的存储结构称为散列表(hash table)。散列表中的一个位置称为槽(slot)。散列技术的核心是散列函数(hash function)。 对任意给定的动态查找表DL,如果选定了某个“理想的”散列函数h及相应的散列表HT,则对DL中的每个数据元素X。函数值h(X.key)就是X在散列表HT中的存储位置。插入(或建表)时数据元素X将被安置在该位置上,并且检索X时也到该位置上去查找。由散列函数决定的存储位置称为散列地址。 因此,散列的核心就是:由散列函数决定关键码值(X.key)与散列地址h(X.key)之间的对应关系,通过这种关系来实现组织存储并进行检索。
二、关键点
我们假设我们申请的是一个HT[N]的内存空间保存我们存储的信息,我们需要保存的信息格式肯定小于等于N个,正常理想的情况,我们存储的N个节点信息分散各个下标的内存空间里,没有冲突,事实hash函数计算的结果不可能这么分散,肯定不同的输入经过hash函数得到相同的值,这样内存就冲突了,所以我们hash算法抓住几个关键点:输入入参,入参关键字,hash函数,hash值,冲突。,其中最核心设计hash函数和解决冲突。
三、散列函数
散列函数有很多方法,具体场景具体使用相应的算法,网上也可以查找很多方法借鉴。散列函数设计的要点尽可能避免冲突,同时冲突桶的深度不能太高,太高的话仍然耗性能。
一般的说,Hash函数可以简单的划分为如下几类:
- 加法Hash;
- 位运算Hash;
- 乘法Hash;
- 除法Hash;
- 查表Hash;
- 混合Hash;
一般工作用的较多的是取模和位运算Hash方法,下面介绍的使用取模方法。
取模:除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = x mod M
位运算Hash:先移位,然后再进行各种位运算是这种类型Hash函数的主要特点
四、冲突解决方法
hash算法另一个难点就是解决冲突,当冲突的实时如何插入管理节点资源,删除,查证。
冲突解决技术可以分为两类:开散列方法( open hashing,也称为拉链法,separate chaining )和闭散列方法( closed hashing,也称为开地址方法,open addressing )
最经典的莫过于拉链法。

拉链法 的实现比较简单,将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可
实现步骤
1)得到一个 key
2)计算 key 的 hashValue
3)根据 hashValue 值定位到 data[hashValue] 。( data[hashValue] 是一条链表)
4)若 data[hashValue] 为空则直接插入
5)不然则添加到链表末尾
另外一个闭散列方法(开放地址法)暂时不介绍了,平时工作用的少,可以百度借鉴其他文章。
五、实战演练
假设有10000个资源需要存储,申请10000个大小的数组,但是怎么根据请求信息来快速定位数组的下标,获取相应的资源信息。我们设计相应的hash算法,有增加,删除,查找接口,满足正常的使用需求。
#include <stdio.h>
#include <string.h>
#include<stdlib.h>
#define HASH_NODE_NUM 10000
#define HASH_MOD_PRIMER ((HASH_NODE_NUM/8)*4+13)
/*链表表头节点*/
typedef struct
{
int hashcount;
int headindex;
int tailindex;
}HASH_BUCKET_HEAD;
typedef struct
{
int id;
int flag;
int preindex;
int nextindex;
}HASH_ITEM_POOL;
int Hash_Find_Node(int id, int flag, HASH_BUCKET_HEAD *pBuckethead,HASH_ITEM_POOL *pItempool)
{
HASH_ITEM_POOL* ptItem = NULL;
HASH_BUCKET_HEAD *pHashHead = NULL;
int key = 0;
int hashcount = 0;
int ntxindex = 0;
if ( NULL == pBuckethead || NULL == pItempool )
{
return -1;
}
key = id % HASH_MOD_PRIMER;
pHashHead = &pBuckethead[key];
hashcount = pHashHead->hashcount;
if(0 == hashcount)
{
return -1;
}
ntxindex = pHashHead->tailindex;
while(hashcount--)
{
if(ntxindex >= HASH_NODE_NUM)
{
return -1;
}
ptItem = &pItempool[ntxindex];
if(id == ptItem->id && flag == ptItem->flag)
{
return ntxindex;
}
if(ntxindex == pHashHead->headindex)
{
break;
}
ntxindex = pItempool->preindex;
}
return -1;
}
int AddIndexToHash(int index, int id, int flag,HASH_BUCKET_HEAD *pBuckethead, HASH_ITEM_POOL *pItempool)
{
HASH_BUCKET_HEAD *pHashHead = NULL;
int key = 0;
int val = -1;
if ( NULL == pBuckethead || NULL == pItempool )
{
return -1;
}
key = id % HASH_MOD_PRIMER;
pHashHead = &pBuckethead[key];
if(NULL == pHashHead)
{
return -1;
}
if(0 == pHashHead->hashcount)
{
pItempool[index].preindex = 0xFFFFFFFF;
pItempool[index].nextindex = 0xFFFFFFFF;
pHashHead->headindex = index;
pHashHead->tailindex = index;
pHashHead->hashcount++;
return 0;
}
val = Hash_Find_Node(id, flag, pBuckethead,pItempool);
if(-1 != val)
{
return -1;
}
pItempool[pHashHead->headindex].preindex = index;
pItempool[index].nextindex = pHashHead->headindex;
pItempool[index].preindex = 0xFFFFFFFF;
pHashHead->headindex = index;
pHashHead->hashcount++;
return 0;
}
int DelIndexFromHash(int index, int id, int flag,HASH_BUCKET_HEAD *pBuckethead, HASH_ITEM_POOL *pItempool)
{
HASH_BUCKET_HEAD *pHashHead = NULL;
HASH_ITEM_POOL *pItem = NULL;
int key = 0;
int val = -1;
if ( NULL == pBuckethead || NULL == pItempool )
{
return -1;
}
pItem = &pItempool[index];
key = id % HASH_MOD_PRIMER;
pHashHead = &pBuckethead[key];
if(NULL == pHashHead)
{
return -1;
}
if(0 == pHashHead->hashcount)
{
return -1;
}
if(0xFFFFFFFF != pItem->preindex)
{
pItempool[pItem->preindex].nextindex = pItem->nextindex;
}
else
{
pHashHead->headindex = pItem->nextindex;
}
if(0xFFFFFFFF != pItem->nextindex)
{
pItempool[pItem->nextindex].preindex = pItem->preindex;
}
else
{
pHashHead->tailindex = pItem->preindex;
}
pItem->preindex = 0xFFFFFFFF;
pItem->nextindex = 0xFFFFFFFF;
pHashHead->hashcount--;
return 0;
}
void Show_Hash_Info(HASH_BUCKET_HEAD *pBuckethead, HASH_ITEM_POOL *pItempool)
{
int i = 0;
for(i= 0;i< HASH_MOD_PRIMER;i++)
{
if(pBuckethead[i].hashcount == 0)
{
printf("index:%d hashcount is 0\n",i);
continue;
}
else
{
printf("index:%d hashcount is %d ",i,pBuckethead[i].hashcount);
int cur = pBuckethead[i].headindex;
while(cur != pBuckethead[i].tailindex)
{
printf("%d--->",cur);
cur = pItempool[cur].nextindex;
}
printf("%d",cur);
}
printf("\n");
}
}
int main()
{
HASH_BUCKET_HEAD *pBuckethead = (HASH_BUCKET_HEAD*)malloc(sizeof(HASH_BUCKET_HEAD) * HASH_MOD_PRIMER);
HASH_ITEM_POOL *pItempool = (HASH_ITEM_POOL*)malloc(sizeof(HASH_ITEM_POOL)*HASH_NODE_NUM);
int i = 0;
int index = -1;
/*初始化hash链表桶*/
for(; i< HASH_MOD_PRIMER; i++)
{
pBuckethead[i].hashcount = 0;
pBuckethead[i].headindex = 0xFFFFFFFF;
pBuckethead[i].tailindex = 0xFFFFFFFF;
}
/*初始化前驱和后继*/
for(; i< HASH_NODE_NUM; i++)
{
pItempool[i].preindex = 0xFFFFFFFF;
pItempool[i].nextindex = 0xFFFFFFFF;
}
/*1.插入节点*/
for(i = 0; i < HASH_NODE_NUM;i++)
{
pItempool[i].id = i*2;
pItempool[i].flag = 10000+i*3;
if(-1 == AddIndexToHash(i, pItempool[i].id, pItempool[i].flag,pBuckethead, pItempool))
{
printf("Add Hash Fail:%d\n",i);
}
}
/*2.打印hash*/
Show_Hash_Info(pBuckethead,pItempool);
printf("-------------------------------\n");
/*3.查找节点*/
index = Hash_Find_Node(100, 10150, pBuckethead,pItempool);
if(-1 != index)
{
printf("index:%d\n",index);
printf("id:%d\n",pItempool[index].id);
printf("flag:%d\n",pItempool[index].flag);
}
else
{
printf("Find Fail!\n");
}
printf("-------------------------------\n");
/*4.删除节点*/
for(i = 0; i < HASH_NODE_NUM;i++)
{
if(-1 == DelIndexFromHash(i,i*2, 10000+i*3,pBuckethead,pItempool))
{
printf("Del Fail:%d!\n",i);
}
}
Show_Hash_Info(pBuckethead,pItempool);
printf("-------------------------------\n");
/*5.在查找节点*/
index = Hash_Find_Node(100, 10150, pBuckethead,pItempool);
if(-1 != index)
{
printf("index:%d\n",index);
printf("id:%d\n",pItempool[index].id);
printf("flag:%d\n",pItempool[index].flag);
}
else
{
printf("Find Fail!\n");
}
/*6.释放内存*/
free(pBuckethead);
pBuckethead = NULL;
free(pItempool);
pItempool = NULL;
return 0;
}
















