#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define DEFAULT_SIZE 16
//哈希表元素定义
typedef struct _ListNode
{
struct _ListNode* next;
int key;//关键值
void* data;//指向数据元素的指针,而不是创建一个数据元素,因为其可能很大
}ListNode;
typedef ListNode* List;//链表
typedef ListNode* Element;//元素,概念不一样而已
//哈希表结构定义
typedef struct _HashTable
{
int TableSize;//桶的大小
List* Thelists;//指针数组,不存储数据,实际指针是int类型的,指向LinkNode类型数据,数组下标为索引
}HashTable;
//哈希函数(映射)(求余多用)
//key可能是字符串
//根据哈希桶数量,根据key的值,定位Hash桶的位置,即根据key值(数据的值)映射到哈希桶的索引号
int Hash(int key, int TableSize)
{
return(key % TableSize);
}
//哈希表接口
//哈希表初始化
HashTable* InitHash(int TableSize)
{
int i = 0;
HashTable* hTable = NULL;//创建Hash表
if (TableSize <= 0) {
TableSize = DEFAULT_SIZE;
}
hTable = (HashTable*)malloc(sizeof(HashTable));//这里分配的Thelists只是一个int类型的变量
if (hTable == NULL)
{
printf("HashTable alloc error.\n");
return NULL;
}
//分配空间后赋值结构体元素
hTable->TableSize = TableSize;
//为Hash桶分配内存空间,其为一个指针数组
hTable->Thelists = (List*)malloc(sizeof(List) * TableSize);//这里分配的是int类型指针变量Thelists指向的具体的空间,因为int类型指针变量Thelists指向的List类型的数据(指向ListNode的指针),因此这里分配的是一个指针数组,也就是好几个桶组成的数组
if (hTable->Thelists == NULL)
{
printf("HashTable malloc error\n");
free(hTable);
return NULL;
}
//为Has桶对应的指针数组初始化链表节点
for (i = 0; i < TableSize; i++)//二级指针Thelists用数组形式使用数据,Thelists[i]就是一个指针
{
hTable->Thelists[i] = (ListNode*)malloc(sizeof(ListNode));//Thelists[i]就是一个指针,指向ListNode结构体,每一个桶是一个链表,分配一个头结点
if (hTable->Thelists[i] == NULL)
{
printf("HashTable malloc error\n");
free(hTable->Thelists);
free(hTable);
return NULL;
}
else
{
memset(hTable->Thelists[i], 0, sizeof(ListNode));//初始化头结点,头结点的元素值全为0,头结点不放元素,只是用于指向首节点
/* hTable->Thelists[i]->key=0;
hTable->Thelists[i]->next=NULL;
hTable->Thelists[i]->data=NULL;*/
}
}
return hTable;
}
//从哈希表中根据键值key查找元素-找到桶号i-找到元素key
Element Find(HashTable* HashTable, int key)
{
int i = 0;
List L=NULL; //用于指向找到的桶号
Element e = NULL; //遍历时暂指向当前元素,先指向首节点
i = Hash(key, HashTable->TableSize);//找到桶号,即key值映射到指针数组的下标为索引
L = HashTable->Thelists[i];
e = L->next; //指向头节点,桶的第一个元素
while (e != NULL && e->key != key)//遍历该桶,找元素
e = e->next;
return e;//key存在就返回对应节点指针,不存在就返回NULL
}
//向哈希表中插入元素,插入的是键值对,即键值及其元素
void Insert(HashTable* HashTable, int key, void* value)//void*类型可以兼容很多数据
{
Element e = NULL, temp = NULL;
List L = NULL;
e = Find(HashTable, key);//找节点,看是否存在
if (e == NULL) //不存在则插入
{
temp = (Element)malloc(sizeof(ListNode));//生成一个节点
if (temp == NULL)
{
printf("malloc error\n");
return ;
}
L = HashTable->Thelists[Hash(key, HashTable->TableSize)];//桶的指针
temp->data = value;//对心生成的节点赋值
temp->key = key;
temp->next = L->next;//前插法 头指针
L->next = temp;
printf("insert success\n");
}
else //存在则不插入
printf("the key already exist\n");
}
//哈希表删除元素,元素为键值对
void Delete(HashTable* HashTable, int key)
{
Element e = NULL, last = NULL; //e暂放当前节点,last暂放前一个结点
List L = NULL; //放头结点
int i = Hash(key, HashTable->TableSize);//找到哈希桶头结点
L = HashTable->Thelists[i];
last = L;//哈希桶指针
e = L->next;//首节点 //首节点才放元素,从头结点开始遍历
while (e != NULL && e->key != key)//遍历查找元素
{
last = e; //保留上一个节点指针,方便后续修改链表结构
e = e->next;
}
if (e)//如果键值对存在
{
last->next = e->next;//修改链表结构
free(e);//删除该元素
}
}
/*
void Delete(HashTable* HashTable, int key) {
Element e = NULL, last = NULL;//结构体指针e
List L = NULL;//哈希桶指针
int i = Hash(key, HashTable->TableSize);
L = HashTable->Thelists[i];
e = L->next;//首结点 存放数据
while (e != NULL && e->key != key) {
last = e;
e = e->next;
}
if (e != NULL) {
if (last != NULL) {
last->next = e->next;
}
else {
L->next = e->next;
}
free(e);
printf("Delete success\n");
}
else {
printf("The key is not exist\n");
}
}
*/
//获取元素的值
void* Retrieve(Element e)
{
return e ? e->data : NULL;
}
int main()
{
char *elems[] = { (char*)"翠花",(char*)"小芳",(char*)"老师" };
int i = 0;
HashTable* HashTable = NULL;//定义一个哈希表
HashTable = InitHash(5);
Insert(HashTable, 1, elems[0]);
Insert(HashTable, 2, elems[1]);
Insert(HashTable, 3, elems[2]);
Delete(HashTable, 1);
for (i = 0; i < 3; i++)
{
Element e = Find(HashTable, i);
if(e){
printf("key:%d,values:%s\n",e->key,(char*)e->data);//Retrieve返回的是void*类型,在这里打印的是字符串型
}
else {
printf("Not found [key:%d]\n", i);
}
}
system("pause");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_SIZE 16
typedef struct _ListNode {
struct _ListNode *next;
int key;
void *data;
} ListNode;
typedef ListNode *List;
typedef ListNode *Element;
typedef struct _HashTable {
int TableSize;
List *Thelists;
} HashTable;
int Hash(int key, int TableSize) {
return key % TableSize;
}
HashTable *InitHash(int TableSize) {
int i;
HashTable *hTable = (HashTable *) malloc(sizeof(HashTable));
if (hTable == NULL) {
printf("HashTable alloc error.\n");
return NULL;
}
if (TableSize <= 0) {
TableSize = DEFAULT_SIZE;
}
hTable->TableSize = TableSize;
hTable->Thelists = (List *) malloc(sizeof(List) * TableSize);
if (hTable->Thelists == NULL) {
printf("HashTable malloc error\n");
free(hTable);
return NULL;
}
for (i = 0; i < TableSize; i++) {
hTable->Thelists[i] = (ListNode *) malloc(sizeof(ListNode));
if (hTable->Thelists[i] == NULL) {
printf("HashTable malloc error\n");
for (int j = i - 1; j >= 0; j--) {
free(hTable->Thelists[j]);
}
free(hTable->Thelists);
free(hTable);
return NULL;
} else {
hTable->Thelists[i]->key = 0;
hTable->Thelists[i]->next = NULL;
hTable->Thelists[i]->data = NULL;
}
}
return hTable;
}
Element Find(HashTable *HashTable, int key) {
int i = 0;
List L = NULL;
Element e = NULL;
i = Hash(key, HashTable->TableSize);
L = HashTable->Thelists[i];
e = L->next;
while (e != NULL && e->key != key) {
e = e->next;
}
return e;
}
void Insert(HashTable *HashTable, int key, void *value) {
Element e = NULL, temp = NULL;
List L = NULL;
e = Find(HashTable, key);
if (e == NULL) {
temp = (Element) malloc(sizeof(ListNode));
if (temp == NULL) {
printf("malloc error\n");
return;
}
L = HashTable->Thelists[Hash(key, HashTable->TableSize)];
temp->data = value;
temp->key = key;
temp->next = L->next;
L->next = temp;
printf("Insert success\n");
} else {
printf("The key already exists\n");
}
}
void Delete(HashTable *HashTable, int key) {
Element e = NULL, last = NULL;
List L = NULL;
int i = Hash(key, HashTable->TableSize);
L = HashTable->Thelists[i];
e = L->next;
while (e != NULL && e->key != key) {
last = e;
e = e->next;
}
if (e != NULL) {
if (last != NULL) {
last->next = e->next;
} else {
L->next = e->next;
}
free(e);
printf("Delete success\n");
} else {
printf("The key is not exist\n");
}
}
void* Retrieve(Element e) {
if (e != NULL) {
return e->data;
} else {
return NULL;
}
}
int main(){
char *elems[] = { (char*)"翠花",(char*)"小芳",(char*)"老师" };
HashTable *hTable = InitHash(3);
Insert(hTable, 1, elems[0]);
Insert(hTable, 2, elems[1]);
Insert(hTable, 3, elems[2]);
Element e = Find(hTable, 1);
Delete(hTable, 1);
for (int i = 0; i < 3; i++) {
Element e = Find(hTable, i + 1);
if (e != NULL) {
printf("key:%d, value:%s", e->key, (char*)e->data);
}
else {
printf("key:%d, value:NULL", i + 1);
}
}
return 0;
}
总结:
①哈希表的结构
②在删除结点时,需要判断链表是否为空,避免链表为空时,对数据进行空操作而造成错误。