哈希表使用链地址法进行数据的存储
哈希桶是在发生冲突时,将数据直接链接在该表位置的后面
当发生冲突时采用链式结构,将相同地址的数链接在后面。适用于经常删除和增加的情况。
当同一地址连接的数据过多时,就会造成效率过低,退化。
#ifndef __hashtablebucket_h__
#define __hashtablebucket_h__
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int HTBKeyType;
typedef int HTBvalueType;
typedef struct HashNode //存放数据节点
{
struct HashNode* next;
HTBKeyType key;
HTBvalueType value;
}HashNode;
typedef struct HashTableBucket //定义哈希桶
{
HashNode** tables;
int size;
int len;
}HTB;
//初始化
void HTBInit(HTB* htb, int len);
//销毁
void HTBDestroy(HTB* htb);
//插入
int HTBInsert(HTB* htb, HTBKeyType key, HTBvalueType value);
//删除
int HTBRemove(HTB* htb, HTBKeyType key);
//查找
HashNode* Find(HTB* htb,HTBKeyType key);
//大小
int HTBsize(HTB* htb);
//判空
int HTBEmpty(HTB* HTB);
#endif
初始化函数
void HTBInit(HTB* htb,unsigned long len)
{
assert(htb);
int i = 0;
// 使用素数表对齐做哈希表的容量,降低哈希冲突
static const unsigned long _PrimeList[28] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for (i=0; i < 28; i++) //选取合适的素数表长
{
if (len < _PrimeList[i])
{
len = _PrimeList[i];
break;
}
}
htb->len = len;
if ((htb->tables = (HashNode**)malloc(sizeof(HashNode*)*htb->len)) == NULL)
return;
htb->size = 0;
for (i = 0; i < htb->len; i++)
htb->tables[i] = NULL;
}
哈希函数
int HashFunc(HTB* htb,HTBKeyType key)
{
return (key%htb->len);
}
分配数据节点空间
HashNode* BuyNode(HTBKeyType key,HTBvalueType value)
{
HashNode* node = malloc(sizeof(HashNode));
node->key = key;
node->next = NULL;
node->value =value;
return node;
}
扩容函数
HashNode* CheckCapacity(HTB* htb)
{
assert(htb);
if ((htb->size / htb->len) * 10 > 7) //当装填因子大于0.7时进行空间的增容
{
int i = 0;
HTB newtables;
HashNode* cur;
HashNode* prev;
int index = 0;
HTB* new ;
HTB* tmp;
new = &newtables;
HTBInit(new, htb->len);//开辟新的空间
if (new != NULL)
{
for (; i < htb->len; i++)
{
cur = htb->tables[i];
while (cur)
{
HTBInsert(new, cur->key, cur->value); //将原空间的数重新转进新开的空间中
cur = cur->next;
}
}
}
tmp =htb;
HTBDestroy(tmp);
htb->tables = new->tables; //将新开的空间给旧的空间
htb->len = new->len;
htb->size = new->size;
}
return htb;
}
插入一个节点
//插入
int HTBInsert(HTB* htb, HTBKeyType key, HTBvalueType value)
{
assert(htb);
int index = 0;
HashNode* tmp;
htb=CheckCapacity(htb); //扩容
index = HashFunc(htb,key); //哈希函数计算位置
tmp = BuyNode(key, value);
if (tmp == NULL)
return -1;
if (htb->tables[index] == NULL) //当第一个节点为空时
htb->tables[index] = tmp;
else //第一个节点不为空,头插
{
tmp->next = htb->tables[index];
htb->tables[index] = tmp;
}
htb->size++;
return 0;
}
删除一个节点
int HTBRemove(HTB* htb, HTBKeyType key)
{
assert(htb);
HashNode* cur=NULL;
HashNode* prev=NULL;
int index;
index = HashFunc(htb, key);
cur = htb->tables[index];
while (cur)
{
if (cur->key == key)
{
if (cur == htb->tables[index]) //当第一个节点为空,直接删除
htb->tables[index] = cur->next;
else
prev->next = cur->next; //第一个节点不为空
free(cur);
cur = NULL;
htb->size--;
return 0;
}
prev = cur;
cur = cur->next;
}
return -1;
}
查找
HashNode* Find(HTB* htb,HTBKeyType key)
{
assert(htb);
int index;
index = HashFunc(htb,key);
HashNode* cur=htb->tables[index]; //确定该数的位置
while (cur)
{
if (cur->key == key)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//打印
void HTBprint(HTB* htb)
{
assert(htb);
HashNode* cur;
int i = 0;
if (htb->size == 0)
return;
for (; i < htb->len; i++)
{
cur = htb->tables[i];
printf("%d: ",i);
while (cur)
{
printf("->%d:%d", cur->key, cur->value);
cur = cur->next;
}
printf("\n");
}
}