哈希桶/开散列法实现哈希表
关于开散列的实现思想见该博客——点击打开链接,这里不再介绍,直接上代码。
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; }
以上函数的测试代码与线性探测法实现的哈希表的测试代码类似。