数据结构之哈希表的实现(开散列)
开散列的方法是,在哈希表中,把数组的每个元素都设置成一个单链表的头节点指针,也可以是不带头节点的单链表
如果出现哈希冲突,就把元素插入那个元素位置的单链表
哈希表结构体的代码如下:
#define HASHMAXSIZE 1000
typedef int HashType;
typedef int ValueType;
typedef size_t(*HashFunc)(HashType);
typedef struct HashElem{
HashType key;
ValueType value;
struct HashElem* _next;
}HashElem;
typedef struct HashTable{
HashElem* data[HASHMAXSIZE];
size_t size;
HashFunc hash_func;
}HashTable;
哈希表的初始化:
把size设置为0,把哈希函数初始化,再把数组里面的每个元素都设置为NULL指针
代码如下:
/*初始化*/
void HashTableInit(HashTable* ht, HashFunc hash_func) {
if (ht == NULL) {
return;
}
int i = 0;
for (; i < HASHMAXSIZE; i++) {
ht->data[i] = NULL;
}
ht->size = 0;
ht->hash_func = hash_func;
}
哈希表的插入:
方法是,找到要插入的数组下标,判断该元素是否存在,如果存在(不包含元素相同的值),
就插入失败,否则,就是往单链表里插入一个元素(建议头插,比较方便)
代码如下:
HashElem* HashTableBucketFind(HashElem* head, HashType key) {
if (head == NULL) {
return NULL;
}
HashElem* cur = head;
while (cur) {
if (cur->key == key) {
return cur;
}
cur = cur->_next;
}
return NULL;
}
HashElem* CreateNewNode(HashType key, ValueType value) {
HashElem* new_node = (HashElem*)malloc(sizeof(HashElem));
if (new_node != NULL) {
new_node->key = key;
new_node->value = value;
new_node->_next = NULL;
}
return new_node;
}
/*插入*/
int HashTableInsert(HashTable* ht, HashType key, ValueType value) {
if (ht == NULL) {
return;
}
size_t offset = ht->hash_func(key);
/*
**找到该元素的存放位置
**遍历链表,判断有没有相同的元素
**如果没有,就头插然后返回1, 如果有,就退出,返回0
*/
HashElem* cur = HashTableBucketFind(ht->data[offset], key);
if (cur == NULL) {
/*表示可以直接插入*/
HashElem* to_insert = CreateNewNode(key, value);
to_insert->_next = ht->data[offset];
ht->data[offset] = to_insert;
++ht->size;
}
else {
/*表示在当前位置有相同的元素,则不能插入*/
return 0;
}
}
哈希表的查找:
这个比较叫简单,用的函数还是上面插入时候查找函数
代码如下:
/*查找*/
int HashTableFind(HashTable* ht, HashType key, ValueType* value) {
if (ht == NULL || value == NULL) {
return 0;
}
size_t offset = ht->hash_func(key);
HashElem* cur = HashTableBucketFind(ht->data[offset], key);
if (cur == NULL) {
/*没找到*/
return 0;
}
else {
*value = cur->value;
return 1;
}
}
哈希表的删除:
删除是,找到该元素,然后删除单链表上的一个节点,这里用的主要是单链表那块的知识,也比较简单
代码如下:
int HashBucketCur_Pre(HashElem* head, HashElem** cur, HashElem** pre, HashType key) {
if (head == NULL) {
return 0;
}
if (cur == NULL || pre == NULL) {
return 0;
}
*cur = head;
while (*cur) {
if ((*cur)->key == key) {
return 1;
}
*pre = *cur;
*cur = (*cur)->_next;
}
return 0;
}
void DestoryHashElem(HashElem* node) {
if (node == NULL) {
return;
}
free(node);
}
/*删除*/
int HashTableRemove(HashTable* ht, HashType key) {
if (ht == NULL) {
return;
}
size_t offset = ht->hash_func(key);
HashElem* pre = NULL;
HashElem* cur = NULL;
int ret = HashBucketCur_Pre(ht->data[offset], &cur, &pre, key);
if (ret == 0) {
/*没有找到*/
printf("该元素不在哈希表中!\n");
return 0;
}
else {
/*找到了*/
if (cur == ht->data[offset]) {
/*要删除的是头节点*/
ht->data[offset] = cur->_next;
DestoryHashElem(cur);
cur = NULL;
--ht->size;
return 1;
}
else {
pre->_next = cur->_next;
DestoryHashElem(cur);
cur = NULL;
--ht->size;
return 1;
}// cur != ht->data[offset]
}//ret != 0
}//函数结束
剩下的几个都是简单的操作,全部代码在下面
我这里实现的是一个简单的哈希表,哈希表这块还需要认真学习,需要拓展一下,比如,如何存储字符串之类的问题
全部代码如下:
HashTable.h
#pragma once
#include<stdio.h>
#include<stddef.h>
#define HASHMAXSIZE 1000
typedef int HashType;
typedef int ValueType;
typedef size_t(*HashFunc)(HashType);
typedef struct HashElem{
HashType key;
ValueType value;
struct HashElem* _next;
}HashElem;
typedef struct HashTable{
HashElem* data[HASHMAXSIZE];
size_t size;
HashFunc hash_func;
}HashTable;
/*初始化*/
void HashTableInit(HashTable* ht, HashFunc hash_func);
/*插入*/
int HashTableInsert(HashTable* ht, HashType key, ValueType value);
/*查找*/
int HashTableFind(HashTable* ht, HashType key, ValueType* value);
/*删除*/
int HashTableRemove(HashTable* ht, HashType key);
/*哈希表中的数据数量*/
size_t HashTableSize(HashTable* ht);
/*哈希表判空*/
int HashTableEmpty(HashTable* ht);
/*销毁哈希表*/
void HashTableDestory(HashTable* ht);
HashTable.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"HashTable(open).h"
#include<stdio.h>
#include<stdlib.h>
/*初始化*/
void HashTableInit(HashTable* ht, HashFunc hash_func) {
if (ht == NULL) {
return;
}
int i = 0;
for (; i < HASHMAXSIZE; i++) {
ht->data[i] = NULL;
}
ht->size = 0;
ht->hash_func = hash_func;
}
HashElem* HashTableBucketFind(HashElem* head, HashType key) {
if (head == NULL) {
return NULL;
}
HashElem* cur = head;
while (cur) {
if (cur->key == key) {
return cur;
}
cur = cur->_next;
}
return NULL;
}
HashElem* CreateNewNode(HashType key, ValueType value) {
HashElem* new_node = (HashElem*)malloc(sizeof(HashElem));
if (new_node != NULL) {
new_node->key = key;
new_node->value = value;
new_node->_next = NULL;
}
return new_node;
}
/*插入*/
int HashTableInsert(HashTable* ht, HashType key, ValueType value) {
if (ht == NULL) {
return;
}
size_t offset = ht->hash_func(key);
/*
**找到该元素的存放位置
**遍历链表,判断有没有相同的元素
**如果没有,就头插然后返回1, 如果有,就退出,返回0
*/
HashElem* cur = HashTableBucketFind(ht->data[offset], key);
if (cur == NULL) {
/*表示可以直接插入*/
HashElem* to_insert = CreateNewNode(key, value);
to_insert->_next = ht->data[offset];
ht->data[offset] = to_insert;
++ht->size;
}
else {
/*表示在当前位置有相同的元素,则不能插入*/
return 0;
}
}
/*查找*/
int HashTableFind(HashTable* ht, HashType key, ValueType* value) {
if (ht == NULL || value == NULL) {
return 0;
}
size_t offset = ht->hash_func(key);
HashElem* cur = HashTableBucketFind(ht->data[offset], key);
if (cur == NULL) {
/*没找到*/
return 0;
}
else {
*value = cur->value;
return 1;
}
}
int HashBucketCur_Pre(HashElem* head, HashElem** cur, HashElem** pre, HashType key) {
if (head == NULL) {
return 0;
}
if (cur == NULL || pre == NULL) {
return 0;
}
*cur = head;
while (*cur) {
if ((*cur)->key == key) {
return 1;
}
*pre = *cur;
*cur = (*cur)->_next;
}
return 0;
}
void DestoryHashElem(HashElem* node) {
if (node == NULL) {
return;
}
free(node);
}
/*删除*/
int HashTableRemove(HashTable* ht, HashType key) {
if (ht == NULL) {
return;
}
size_t offset = ht->hash_func(key);
HashElem* pre = NULL;
HashElem* cur = NULL;
int ret = HashBucketCur_Pre(ht->data[offset], &cur, &pre, key);
if (ret == 0) {
/*没有找到*/
printf("该元素不在哈希表中!\n");
return 0;
}
else {
/*找到了*/
if (cur == ht->data[offset]) {
/*要删除的是头节点*/
ht->data[offset] = cur->_next;
DestoryHashElem(cur);
cur = NULL;
--ht->size;
return 1;
}
else {
pre->_next = cur->_next;
DestoryHashElem(cur);
cur = NULL;
--ht->size;
return 1;
}// cur != ht->data[offset]
}//ret != 0
}//函数结束
/*哈希表中的数据数量*/
size_t HashTableSize(HashTable* ht) {
if (ht == NULL) {
return 0;
}
return ht->size;
}
/*哈希表判空*/
int HashTableEmpty(HashTable* ht) {
if (ht == NULL) {
return 1;
}
int i = 0;
for (; i < HASHMAXSIZE; i++) {
if (ht->data[i] != NULL) {
return 0;
}
}
return 1;
}
/*销毁哈希表*/
void HashTableDestory(HashTable* ht) {
if (ht == NULL) {
return;
}
int i = 0;
for (; i < HASHMAXSIZE; ++i) {
if (ht->data[i] != NULL) {
/*表示该位置有元素,遍历然后删除*/
HashElem* cur = ht->data[i];
HashElem* to_delte = NULL;
while (cur) {
to_delte = cur;
cur = cur->_next;
DestoryHashElem(to_delte);
to_delte = NULL;
ht->data[i] = NULL;
}//cur == NULL
}//ht->data[i] == NULL
}//i >= HASHMAXSIZE
ht->size = 0;
ht->hash_func = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"HashTable(open).h"
#include<stdio.h>
#include<stdlib.h>
size_t hash_func(HashType key) {
return key % HASHMAXSIZE;
}
#define TESTHEAD printf("------------%s------------\n",__FUNCTION__)
void HashTablePrint(HashTable* ht) {
if (ht == NULL) {
return;
}
int i = 0;
for (; i < HASHMAXSIZE; ++i) {
if (ht->data[i] != NULL) {
HashElem* cur = ht->data[i];
while (cur) {
printf("[%d]: key = %4d, value = %5d, _next-> ", i, cur->key, cur->value);
cur = cur->_next;
}
printf("NULL\n");
}
}
}
void TestInit() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
int i = 0;
for (; i < HASHMAXSIZE; ++i) {
if (ht.data[i] != NULL) {
printf("index = %d 的位置未被初始化\n", i);
}
}
printf("expect 0, actual:%d\n", ht.size);
printf("expect %p, actual:%p\n", hash_func, ht.hash_func);
}
void TestInsert() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
HashTableInsert(&ht, 101, 100);
HashTableInsert(&ht, 102, 200);
HashTableInsert(&ht, 1001, 300);
HashTableInsert(&ht, 1001, 400);
HashTableInsert(&ht, 1002, 500);
HashTableInsert(&ht, 1, 100);
HashTableInsert(&ht, 2, 200);
HashTablePrint(&ht);
}
void TestFind() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
HashTableInsert(&ht, 101, 100);
HashTableInsert(&ht, 102, 200);
HashTableInsert(&ht, 1001, 300);
HashTableInsert(&ht, 1001, 400);
HashTableInsert(&ht, 1002, 500);
HashTableInsert(&ht, 1, 600);
HashTableInsert(&ht, 2, 700);
HashTablePrint(&ht);
ValueType value = -1;
int ret = HashTableFind(&ht, 101, &value);
printf("expect 1, actual:%d\n", ret);
printf("expect 100, actual:%d\n", value);
int ret2 = HashTableFind(&ht, 103, &value);
printf("expect 0, actual:%d\n", ret2);
int ret3 = HashTableFind(&ht, 1002, &value);
printf("expect 1, actual:%d\n", ret3);
printf("expect 500, actual:%d\n", value);
}
void TestRemove() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
HashTableInsert(&ht, 1001, 300);
HashTableInsert(&ht, 1001, 400);
HashTableInsert(&ht, 1002, 500);
HashTableInsert(&ht, 1, 600);
HashTableInsert(&ht, 2, 700);
printf("删除前:\n");
HashTablePrint(&ht);
/*测试删除一个头节点元素*/
printf("删除后:\n");
HashTableRemove(&ht, 1);
HashTablePrint(&ht);
/*测试删除不存在的元素*/
printf("测试删除哈希表中不存在的元素:\n");
HashTableRemove(&ht, 1);
HashTablePrint(&ht);
/*测试删除正常元素*/
printf("测试删除正常元素:\n");
HashTableRemove(&ht, 1001);
HashTableRemove(&ht, 1002);
HashTablePrint(&ht);
}
void TestHash_Size_Empty() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
int ret = HashTableEmpty(&ht);
printf("expect 1, actual:%d\n", ret);
HashTableInsert(&ht, 1001, 300);
HashTableInsert(&ht, 1001, 400);
HashTableInsert(&ht, 1002, 500);
HashTableInsert(&ht, 1, 600);
HashTableInsert(&ht, 2, 700);
printf("删除前:\n");
HashTablePrint(&ht);
ret = HashTableEmpty(&ht);
printf("expect 0, actual:%d\n", ret);
size_t sz = HashTableSize(&ht);
printf("expect 4, actual:%d\n", sz);
}
void TestDestory() {
HashTable ht;
TESTHEAD;
HashTableInit(&ht, hash_func);
HashTableInsert(&ht, 1001, 100);
HashTableInsert(&ht, 1001, 200);
HashTableInsert(&ht, 1002, 300);
HashTableInsert(&ht, 1, 400);
HashTableInsert(&ht, 2, 500);
HashTableInsert(&ht, 3, 600);
HashTableInsert(&ht, 4, 700);
HashTableInsert(&ht, 5, 800);
HashTableInsert(&ht, 6, 900);
HashTableInsert(&ht, 7, 1000);
HashTablePrint(&ht);
HashTableDestory(&ht);
}
int main() {
TestInit();
TestInsert();
TestFind();
TestRemove();
TestHash_Size_Empty();
TestDestory();
system("pause");
return 0;
}