开散列:(链地址法也叫做开链法)首先对关键码集合用哈希函数计算哈希地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,每个桶中的元素通过单链表链接起来,链表的头结点存储在哈希表中,由此有一个形象的叫法叫做哈希桶。
test.h的实现
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
//我们在这里的hash表期望存储的数据是键值对这样的结构
#define HashMaxSize 1000
typedef enum Stat{
empty,//空状态
valid,//有效状态
deleted,//删除状态
}Stat;
typedef int Hashtype;
typedef int keytype;
typedef int valtype;
typedef size_t(*HashFunc)(keytype key);
//这个结构表示的是哈希表中的一个元素
这个元素同时包含了键值对
typedef struct Hashelme{
keytype key;
valtype value;
struct Hashelme* next;
}Hashelme;
typedef struct Hashtable{
//如果我们的hash桶中的链表是一个不带头结点的链表
//我们就是用Hashelme*
//如果是一个带头结点的链表
//我们就直接用Hashelme
Hashelme *data[HashMaxSize];
size_t size;//哈希表当中元素的个数(【0,size)不能表示哈希表有效元素的区间
HashFunc func;//这是一个函数指针,指向的是hash函数
}Hashtable;
void HashInit(Hashtable* ht);
void HashDestroy(Hashtable* ht);
void HashInsert(Hashtable* ht,keytype key,valtype value);
int HashFind(Hashtable* ht,keytype key,valtype* value);
void HashRemove(Hashtable* ht,keytype);
test.c的实现
#include "hash2.h"
//哈希函数
size_t HashDefault(keytype key)
{
return key%HashMaxSize;
}
//初始化
void HashInit(Hashtable* ht)
{
if(ht==NULL)
{
return;
}
ht->size=0;
ht->func=HashDefault;
size_t i=0;
for(;i<HashMaxSize;i++)
{
ht->data[i]=NULL;
}
return;
}
void HashDestroyNode(Hashelme* p)
{
free(p);
p=NULL;
}
//销毁
void HashDestroy(Hashtable* ht)
{
if(ht==NULL)
{
return;
}
ht->size=0;
ht->func=NULL;
//遍历所有的链表并且进行释放
size_t i=0;
for(;i<HashMaxSize;i++)
{
Hashelme* cur=ht->data[i];
if(cur==NULL)
{
continue;
}
while(cur!=NULL)
{
Hashelme* pre=cur;
HashDestroyNode(cur);
cur=pre->next;
}
return;
}//退出for循环
}
Hashelme* HashBucketFind(Hashelme* head, keytype to_find)
{
Hashelme* cur=head;
for(;cur!=NULL;cur=cur->next)
{
if(cur->key==to_find)
{
break;
}
}
return cur;
}
Hashelme* Createelme(keytype key,valtype value)
{
Hashelme* newelme=(Hashelme*)malloc(sizeof(Hashelme));
newelme->key=key;
newelme->value=value;
return newelme;
}
void HashInsert(Hashtable* ht,keytype key,valtype value)
{
if(ht==NULL)
{
return;
}
if(ht->size>0.8*HashMaxSize)
{
//哈希表中的有效元素已经超过了负载因子的上限、
//所以不能再继续的进行插入元素了
return;
}
//走到这里说明我们还能继续进行插入元素,所以
//我们首先应该计算出offset的值
size_t offset=HashDefault(key);
//在offset对应的链表中查找当前的key是否存在,如果
//存在就认为查找失败
Hashelme* ret=HashBucketFind(ht->data[offset],key);
if(ret!=NULL)
{
//走到这里我们认为插入失败,所要插入的元素已经存在
//插入元素失败,返回
return;
}
//走到这里说明我们所要插入的元素不存在,就可以插入(使用头插的方法)
Hashelme* new_elme=Createelme(key,value);
//将新元素插入到当前的链表当中
new_elme->next=ht->data[offset];
//更新头结点的值为我们新插入的元素
ht->data[offset]=new_elme;
//给size进行+1操作
++ht->size;
}
void HashPrint(Hashtable* ht,const char* msg)
{
printf("[%s]\n",msg);
size_t i=0;
for(;i<HashMaxSize;i++)
{
if(ht->data[i]!=NULL)
{
printf("i=%lu\n",i);
Hashelme* cur=ht->data[i];
for(;cur!=NULL;cur=cur->next)
{
printf("(%d:%d)\n",cur->key,cur->value);
}
}//if循环结束
}//for循环结束
printf("\n");
}
//查找元素是否存在于对应的链表中的函数,成功的话返回元素的节点
int HashFind(Hashtable* ht,keytype key,valtype* value)
{
if(ht==NULL || value==NULL)
{
return 0;
}
if(ht->size==0)
{
return 0;
}
//1、计算出offset的值
size_t offset=HashDefault(key);
//2、找到offset的链表,遍历链表尝试找到其中的元素
Hashelme* ret=HashBucketFind(ht->data[offset],key);
if(ret==NULL)
{
//没有找到
return 0;
}
else
{
//找到了
*value=ret;
return *value;
}
}
int HashBucketFindEx(Hashelme* head,keytype to_find,Hashelme** prenode,Hashelme** curnode)
{
Hashelme* cur=head;
Hashelme* pre=NULL;
for(;cur!=NULL;pre=cur,cur=cur->next)
{
if(cur->key==to_find)
{
//说明找到了
break;
}
}
if(cur==NULL)
{
//说明链表已经遍历完毕了
return 0;
}
*prenode=pre;
*curnode=cur;
return 1;
}
void HashRemove1(Hashtable* ht,keytype key)
{
if(ht==NULL)
{
return;
}
if(ht->size==0)
{
return;
}
//1、根据key计算出offset
size_t offset=HashDefault(key);
//2、通过offset找出相应的链表,在链表当中找到指定的元素
//并且进行删除
Hashelme* pre=NULL;
Hashelme* cur=NULL;
int ret=HashBucketFindEx(ht->data[offset],key,&pre,&cur);
if(ret==0)
{
return;
}
else
{
//找到了
if(pre==NULL)
{
//pre本来就是cur的前一个元素,那么我们要删除的就是头结点
//我们必须更新头结点的指针
ht->data[offset]=cur->next;
}
else
{
//cur 为待删除的元素,pre->next本来指向的是cur
//如果cur被删除了,现在pre指向的是cur的下一个元素
pre->next=cur->next;
}
HashDestroyNode(cur);
//3、--size
--ht->size;
}
}
void HashRemove2(Hashtable* ht,keytype key)
{
if(ht==NULL)
{
return;
}
if(ht->size==0)
{
return;
}
//1、根据key计算出offset
size_t offset=HashDefault(key);
//2、通过offset找出相应的链表,在链表当中找到指定的元素
//并且进行删除
Hashelme* pre=NULL;
Hashelme* cur=ht->data[offset];
while(cur!=NULL)
{
if(cur->key==key)
{
break;
}
pre=cur;
cur=cur->next;
}
if(cur==NULL)
{
return;
}
if(pre==NULL)
{
Hashelme* p=ht->data[offset];
ht->data[offset]=p->next;
HashDestroyNode(p);
}
else
{
pre->next=cur->next;
HashDestroyNode(cur);
}
}
/////////////////////////////////////////////////////
/////////////////////////test///////////////////////
///////////////////////////////////////////////////
#define TEST_HEADER printf("\n=======%s=======\n",__FUNCTION__);
void test1()
{
TEST_HEADER;
Hashtable ht;
HashInit(&ht);
printf("expect size is 0,actual size is %d\n",ht.size);
printf("expect func is %p,actual func is %p",HashDefault,ht.func);
}
void test2()
{
TEST_HEADER;
Hashtable ht;
HashInit(&ht);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,20);
HashInsert(&ht,1000,100);
HashInsert(&ht,2000,200);
HashPrint(&ht,"插入五个元素");
}
void test3()
{
TEST_HEADER;
Hashtable ht;
HashInit(&ht);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,20);
HashInsert(&ht,1000,100);
HashInsert(&ht,2000,200);
HashPrint(&ht,"插入五个元素");
valtype value;
int ret=0;
ret= HashFind(&ht,1000,&value);
printf("查找数据’1000‘的结果是: \n");
printf("expect ret is 1,actual ret is %d\n",ret);
printf("expect value is 10,actual value is %d\n",value);
ret= HashFind(&ht ,4000,&value);
printf("查找数据’4000‘的结果是: \n");
printf("expect ret is 0,actual ret is %d\n",ret);
}
void test4()
{
TEST_HEADER;
Hashtable ht;
HashInit(&ht);
HashInsert(&ht,1,1);
HashInsert(&ht,1,10);
HashInsert(&ht,2,20);
HashInsert(&ht,1000,100);
HashInsert(&ht,2000,200);
HashPrint(&ht,"插入五个元素");
HashRemove2(&ht,1);
HashPrint(&ht,"删除数据为1的元素后的结果为:");
HashRemove2(&ht,2);
HashPrint(&ht,"删除数据为2的元素后的哈希表:");
}
int main()
{
test1();
test2();
test3();
test4();
return;
}