哈希桶/开散列法实现哈希表
关于开散列的实现思想见该博客——点击打开链接,这里不再介绍,直接上代码。
1. 哈希表结构实现
//哈希桶存放数据
#pragma once
#include <stdio.h>
#include <stdlib.h>
#define SHOW_NAME printf("\n====================%s====================\n", __FUNCTION__);
#define HASHMAXSIZE 1000
typedef int KeyType;
typedef int ValType;
typedef size_t (*HashFunc)(KeyType key);//函数指针,控制元素存入位置
typedef struct HashElem//相当于链表的结点
{
KeyType key;
ValType val;
struct HashElem* next;//指向下一个元素
}HashElem;
typedef struct HashTable
{
HashElem* data[HASHMAXSIZE];//哈希表不带头结点,所以用HashElem*型;若哈希表带头结点,用HashElem型,与链表原理一样
HashFunc func;
size_t size;//表示当前哈希表中有效元素的个数
}HashTable;
其中哈希表中每个元素存放一个结点的指针,用以表示整个链表,函数指针用来调用哈希函数控制存入位置,size用来统计当前哈希表中的有效元素个数。每个链表中的每个结点由一个键值对、一个指向下一个结点的next指针构成。
2.初始化
//1.初始化
size_t HashFuncDefault(KeyType key)//
{
return key%HASHMAXSIZE;
}
void HashInit(HashTable* ht, HashFunc func)//初始化
{
if(ht == NULL)
return;
ht->size = 0;
ht->func = func;
size_t i = 0;
for(i=0; i<HASHMAXSIZE; ++i)
{
ht->data[i] = NULL;
}
return;
}
3. 销毁
//2.销毁
void ElemDestroy(HashElem* node)//销毁结点
{
free(node);
return;
}
void HashDestroy(HashTable* ht)//销毁哈希表
{
if(ht == NULL)
return;
ht->size = 0;
ht->func = NULL;
size_t i = 0;
for(i=0; i<HASHMAXSIZE; ++i)
{
HashElem* cur = ht->data[i];
while(cur != NULL)
{
HashElem* next = cur->next;
ElemDestroy(cur);
cur = next;
}
}
return;
}
4. 打印函数,用于测试
这里注意只打印不为空的链表。
//3.打印函数,用于测试
void HashPrint(HashTable* ht, const char* msg)//打印函数
{
printf("[%s]\n", msg);
if(ht == NULL)
return;
size_t i = 0;
for(; i<HASHMAXSIZE; ++i)
{
if(ht->data[i] == NULL)
continue;
printf("i = %d\n", i);
HashElem* cur = ht->data[i];
while(cur != NULL)
{
printf("[%d:%d] ", cur->key, cur->val);
cur = cur->next;
}
printf("\n");
}
return;
}
5. 创建链表的新结点
//4.创建链表的一个结点
HashElem* ElemCreate(KeyType key, ValType val)
{
HashElem* new_node = (HashElem*)malloc(sizeof(HashElem));
new_node->key = key;
new_node->val = val;
new_node->next = NULL;
return new_node;
}
6. 插入操作
(1)根据key值计算插入位置offset
(2)在offset对应的链表找要插入的key
(3)在链表中找到了要插入的key,我们约定为插入失败
(4)若没有要插入的key,就将元素插入到链表中
(5)插入结束后++size
//5.插入操作
HashElem* HashBucketFind(HashElem* head, KeyType key)//查找链表中是否存在当前要插入的元素
{
HashElem* cur = head;
while(cur != NULL)
{
if(key == cur->key)
return cur;
cur = cur->next;
}
return NULL;
}
void HashInsert(HashTable* ht, KeyType key, ValType val)//插入
{
if(ht == NULL)//非法输入
return;
//1.根据key计算插入位置offset
size_t offset = ht->func(key);
//2.在offset对应链表查找要插入的key
HashElem* ret = HashBucketFind(ht->data[offset], key);
//3.若key存在,插入失败
if(ret != NULL)
return;
//4.若key不存在,可进行插入操作(头插)
HashElem* new_node = ElemCreate(key, val);
new_node->next = ht->data[offset];
ht->data[offset] = new_node;
//5.插入结束,++size
++ht->size;
return;
}
7. 查找操作
(1)根据key值计算当时的插入位置offset
(2)在offset对应的链表查找key
(3)在链表中找到了查找的key,返回对应的val,查找成功
(4)若链表中没有查找的key,查找失败
//5.给定一个key,查找对应的val
int HashFind(HashTable* ht, KeyType key, ValType* val)//查找
{
if(ht == NULL || val == NULL)//非法操作
return 0;
if(ht->size == 0)//哈希表为空
return 0;
//1.根据key计算offset
size_t offset = ht->func(key);
//2.在offset对应的链表中遍历查找当前要查找的元素
HashElem* ret = HashBucketFind(ht->data[offset], key);
if(ret == NULL)
return 0;
*val = ret->val;
return 1;
}
8. 删除操作
(1)根据key值计算当时的插入位置offset
(2)在offset对应的链表查找key
(3)在链表中找到了要删除的key,删除对应的结点,删除成功
(4)若链表中没有查找的key,删除失败
(5)删除结束后,--size
//6.给定一个key,删除对应元素
int HashBucketFindEx(HashElem* head, KeyType to_find, HashElem** pre, HashElem** to_delete)//查找要删除元素及其父结点
{
if(head == NULL || pre == NULL || to_delete == NULL)//非法输入
return 0;
HashElem* ret = head;
HashElem* ex = NULL;
for(; ret!=NULL; ret=ret->next)
{
if(ret->key == to_find)
break;
}
if(ret == NULL)
return 0;
*pre = ex;
*to_delete = ret;
return 1;
}
void HashRemove(HashTable* ht, KeyType key)//删除操作
{
if(ht == NULL)//非法操作
return;
if(ht->size == 0)//空哈希表
return;
//1.根据key计算offset
size_t offset = ht->func(key);
//2.在offset对应链表查找要删除元素
HashElem* pre;
HashElem* to_remove;
int ret = HashBucketFindEx(ht->data[offset], key, &pre, &to_remove);
//3.没找到要删除元素
if(ret == 0)
return;
//4.要删除元素为链表首元素结点
if(pre == NULL)
ht->data[offset] = to_remove->next;
//5.要删除元素为链表中一个结点
else
pre->next = to_remove->next;
ElemDestroy(to_remove);
//6.删除操作结束后--size
--ht->size;
return;
}
以上函数的测试代码与线性探测法实现的哈希表的测试代码类似。