【数据结构】简单哈希表的实现(开散列)

数据结构之哈希表的实现(开散列)


开散列的方法是,在哈希表中,把数组的每个元素都设置成一个单链表的头节点指针,也可以是不带头节点的单链表


如果出现哈希冲突,就把元素插入那个元素位置的单链表


哈希表结构体的代码如下:

#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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值