一、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函数可以简单的划分为如下几类:

  1. 加法Hash;
  2. 位运算Hash;
  3. 乘法Hash;
  4. 除法Hash;
  5. 查表Hash;
  6. 混合Hash;

一般工作用的较多的是取模和位运算Hash方法,下面介绍的使用取模方法。
取模:除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = x mod M
位运算Hash:先移位,然后再进行各种位运算是这种类型Hash函数的主要特点

四、冲突解决方法

hash算法另一个难点就是解决冲突,当冲突的实时如何插入管理节点资源,删除,查证。

冲突解决技术可以分为两类:开散列方法( open hashing,也称为拉链法,separate chaining )和闭散列方法( closed hashing,也称为开地址方法,open addressing )

最经典的莫过于拉链法。

hive用hash函数分成100份 hive hash函数算法_散列函数


拉链法 的实现比较简单,将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可

实现步骤

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;
}