我们在这篇博客 哈希表初探
已经初步了解了哈希表的作用,那么接下来就应该自己实现一下哈希表了.
同样的,实现两种不同的解决哈希冲突的方案1,闭散列 2,哈希桶
闭散列
头文件
#pragma once
#include<stdio.h>
//这个结构体表示哈希表中的一个元素
//这个元素中同时包含了键值对
typedef int KeyType;
typedef char ValType;
typedef enum State
{
Empty, //空状态
Vaild,//有效状态
Deleted,//服务于remove
}State;
struct HashTable;
typedef size_t (*HashFunc)(KeyType key,struct HashTable* );
typedef struct HashElem{
KeyType key;
ValType val;
//每个hashtable内元素的状态``
State state;
}HashElem;
typedef struct HashTable{
HashElem* data;
size_t size;
size_t capacity;
// 填装因子
float Load_Factor;
//哈希函数
HashFunc func;
}HashTable;
void HashInit(HashTable* ht,HashFunc hash_func);
void HashDestory(HashTable* ht);
void HashInsert(HashTable* ht,HashElem elem);
ValType HashFind(HashTable* ht, KeyType key);
size_t hash_func( KeyType key,HashTable*ht);
void HashRemove(HashTable* ht, KeyType key);
void HashPrint(HashTable* ht);
各个函数的实现
void HashInit(HashTable* ht,HashFunc hash_func);
初始化哈希表的工作很简单,分配存储哈希元素的数组,然后初始化 哈希表内各个成员的初始值,
值得注意的是我们在申请哈希表空间之后,数组内的元素是随机值,我们要标识该元素是否有效,那么得把数组内每个元素的状态都至为Empty,(简单的把元素都至为0是不行的,万一要插入的key是0,那么会出现此位置已经被占用的错误)
size_t GetHashTableCapacity(HashTable* ht)
{
if(ht == NULL)
return -1;
return ht->capacity;
}
size_t hash_func( KeyType key,HashTable*ht)
{
return key % GetHashTableCapacity(ht);
}
void HashInit(HashTable* ht,HashFunc hash_func)
{
if(ht == NULL || hash_func == NULL)
return;
ht->capacity = 1000;
ht->data= (HashElem*) malloc(sizeof(size_t)* ht->capacity);
ht->size = 0;
ht->func = hash_func;
size_t i = 0;
for( ; i < ht->size; i++ )
{
ht->data[i].state = Empty;
}
}
void HasDestory(HashTable* ht)
销毁很简单,不提
void HashInsert(HashTable* ht,HashElem elem)
在哈希表内插入元素,需要考虑的问题有
1.哈希表满了,2插入的key已经存在,3哈希冲突的问题
void HashInsert(HashTable* ht,HashElem elem)
{
KeyType key = elem.key;
if(ht == NULL)
return;
if(ht->size >= ht->capacity * ht->Load_Factor)
{
//哈希表已经满了
return;
}
size_t offset = (*ht->func)(key,ht);
//哈希冲突
while(ht->data[offset].state == Vaild )
{
offset++;
//在占用中找到了重复元素
if(ht->data[offset].key == key)
{
//重复元素
printf("重复元素\n");
return;
}
//从头开始找
if( offset == ht->capacity )
{
//有填充因子存在 不会死循环
offset = 0;
}
}
//找到第一个状态不是 已经占用的位置插入
ht->data[offset] = elem;`
ht->data[offset].state = Vaild;
ht->size++;
}
ValType HashFind(HashTable* ht, KeyType key)
void HashRemove(HashTable* ht, KeyType key)
查找要注意的点是:哈希冲突,往后如果找到empty还没找到key,就表明key不在哈希表中,
因为这一点,当我们删除时不能将key的状态置为empty,这样会使再次查找key以后的元素失败,所以我们应该为删除元素独立设立一个状态Deleted
ValType HashFind(HashTable* ht, KeyType key)
{
if(ht == NULL)
return -1;
size_t offset = (*hash_func)(key,ht);
//不为空但是又不是 key offset ++
while(1)
{
//碰到查找的元素是delete状态
//其实可以忽略,往后找空的就行
if(ht->data[offset].state == Empty)
{
printf("not found \n");
return -1;
}
//排除delete状态
if(ht->data[offset].key == key && ht->data[offset].state == Vaild )
{
return ht->data[offset].val;
}
offset++;
if(offset == ht->capacity)
offset = 0;
}
//遇到删除的状态还得往后找(删除后面元素的状态可能是vaild)
//只有遇到空状态才表示查找失败(空表示此位置肯定没有进行插入,它后面的元素就不可能是哈希冲突造成的vaild状态)
}
void HashRemove(HashTable* ht, KeyType key)
{
if(ht == NULL)
return ;
size_t offset = (*hash_func)(key, ht);
while(ht->data[offset].state != Empty)
{
if(ht->data[offset].key == key && ht->data[offset].state == Vaild)
{
ht->data[offset].state = Deleted;
return;
}
offset++;
if(offset == ht->capacity)
{
offset = 0;
}
}
printf("not found \n");
return;
}
void HashPrint(HashTable* ht)
{
if(ht == NULL)
return;
size_t i = 0;
for( ; i < ht->capacity; i++ )
{
if(ht->data[i].state != Vaild)
{
continue;
}
printf("[%lu|%d|%d] " ,i, ht->data[i].key, ht->data[i].val);
}
printf("\n");
}
哈希桶
#pragma once
#include<stdio.h>
#define HashMaxSize 1000
typedef int KeyType;
typedef char ValType;
typedef size_t (*HashFunc)(KeyType key );
typedef struct HashEelem{
KeyType key;
ValType val;
struct HashEelem* next;
}HashEelem;
typedef struct HashTable{
HashEelem* data[HashMaxSize];
size_t size;
//哈希桶不需要填装因子和状态枚举
HashFunc func;
}HashTable;
size_t hash_func( KeyType key);
void HashInit(HashTable* ht,HashFunc hash_func);
void HashDestory(HashTable* ht);
void HashInsert(HashTable* ht,HashEelem elem);
ValType HashFind(HashTable* ht, KeyType key);
size_t hash_func( KeyType key);
void HashRemove(HashTable* ht, KeyType key);
void HashPrint(HashTable* ht);
#include"hash.h"
#include<stdlib.h>
#include<unistd.h>
size_t hash_func( KeyType key)
{
return key % HashMaxSize;
}
void HashInit(HashTable* ht,HashFunc hash_func)
{
if(ht == NULL)
return;
int i = 0;
for( ; i < HashMaxSize; i++ )
{
ht->data[i] = NULL;
}
ht->size = 0;
ht->func = hash_func;
}
void HashDestory(HashTable* ht)
{
if(ht == NULL)
return;
size_t i = 0;
for(; i < HashMaxSize; i++)
{
HashEelem* cur = ht->data[i];
HashEelem* to_delete = NULL;
while(cur)
{
to_delete = cur;
cur= cur->next;
free(to_delete);
to_delete = NULL;
}
}
ht->size = 0;
}
HashEelem* CreateNode(KeyType key, ValType val)
{
HashEelem* elem = (HashEelem*)malloc(sizeof(HashEelem));
if(elem == NULL)
return NULL;
elem->key = key;
elem->val = val;
elem->next = NULL;
return elem;
}
void HashInsert(HashTable* ht,HashEelem elem)
{
if(ht == NULL)
return;
size_t offset = ht->func(elem.key);
if(ht->data[offset] == NULL)
{
ht->data[offset] = CreateNode(elem.key,elem.val);
}
else
{
HashEelem* new_node= CreateNode(elem.key,elem.val);
new_node->next = ht->data[offset];
ht->data[offset] = new_node;
}
ht->size++;
}
ValType HashFind(HashTable* ht, KeyType key)
{
if(ht == NULL)
return -1;
size_t offset = ht->func(key);
HashEelem* cur = ht->data[offset];
while(cur)
{
if(cur->key == key)
{
return cur->val;
}
cur = cur->next;
}
printf("not found\n");
return -1;
}
void HashRemove(HashTable* ht, KeyType key)
{
if(ht == NULL)
return;
size_t offset = ht->func(key);
HashEelem* cur = ht->data[offset];
HashEelem* pre_cur = NULL;
while(cur)
{
if( cur->key == key )
{
break;
}
pre_cur = cur;
cur = cur->next;
}
if(cur == NULL)
{
printf("empty key \n");
}
else if(pre_cur == NULL)
{
free(cur);
cur = NULL;
}
else
{
free(cur);
cur = NULL;
pre_cur->next = NULL;
}
}
void HashPrint(HashTable* ht)
{
size_t i = 0;
for(; i < HashMaxSize; i++)
{
if( ht->data[i] == NULL )
continue;
HashEelem* cur = ht->data[i] ;
printf("第%lu条链表结构如下\n",i);
while(cur)
{
/*sleep(1);*/
printf("[key: %d| val: %d] ", cur->key,cur->val);
cur = cur->next;
}
printf("\n");
}
printf("\n");
}