当你看这篇的时候我认为你已经懂得哈希表的基本原理和一些具体方法实现了,如果你是想清晰的理解哈希表原理,点这个哈希表(散列表)原理详解
闭散列
- 我们往哈希表中插入数据时往往会发生哈希冲突,即两个不一样的
key
通过散列函数求出的下标offset
是一样的,这时候就要给后插入的数据重新找位置,就诞生了两种形式,闭散列和开散列 - 闭散列又称线性探测,即如果当前往哈希表中插入数据产生哈希冲突,就需要把新插入的数据另辟地方存储。
- 举个例子:把哈希表看成是摆成一排的一个一个的箱子,箱子中存放的是数据,当我们往目标箱子中放新数据时,发现该箱子中已经有数据了,那就看看它旁边箱子中空着没,空着的话就存进去数据,没空的话就再扩大范围找,不过这个范围和规律要自己定义,不是随随便便的放。
- 我这里采用的方法是如果发现所求下标
offset
处已经有数据了,则offset++
往后探测,直到找到空箱子。
代码以及解释
- 结构体声明
//键值对
typedef int KeyType;
typedef int ValueType;
//哈希函数指针
typedef int (*HashFunc)(KeyType key);
//哈希表中元素的状态
typedef enum State{
EMPTY,//当前点是空的
VALUE,//当前点是有元素的
DELETED//当前点已被删除,也可以当做空了
}State;
//将键值对存放于结构体中
typedef struct KeyValue{
KeyType key;
ValueType value;
State state;
}KeyValue;
//哈希表
typedef struct HashTable{
KeyValue data[HashMaxSize];
size_t size;
HashFunc func;
}HashTable;
- 初始化哈希表和销毁哈希表
//哈希函数(取余散列法)
int HashFunction(KeyType key)
{
return key % HashMaxSize;
}
//哈希表初始化
void HashInit(HashTable* ht ,HashFunc func)
{
if(ht == 0)
{
//非法输入
return;
}
ht->size = 0;
ht->func = func;
size_t i = 0;
for(; i < HashMaxSize; i++)
{
ht->data[i].state = EMPTY;
}
}
//销毁哈希表
void HashDestroy(HashTable* ht)
{
if(ht == 0)
{
return;
}
ht->size = 0;
ht->func = NULL;
}
- 插入和删除操作
//往哈希表中插入数据
void HashInsert(HashTable* ht, KeyType key, ValueType value)
{
if(ht == NULL