哈希表的结构是这样的
//HashTable.h
#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_
#include <stdlib.h>
#include <stdbool.h>
#define BUCKET_SIZE 5 //基桶与溢出桶大小,此处的值表示可容纳5个元素
#define HASHTABLE_SIZE 17 //哈希表中基桶个数,为了使得出的哈希因子在所有可得的值中得出任一值的概率相同,所以使用素数作为基桶个数
#define INVALID_VALUE -11111 //无效标记
typedef int EleType;
typedef int KeyType;
struct _ElementNode_ //元素节点类型,键值对
{
KeyType Key;
EleType Val;
};
//存储键值对,Hash与之前的平衡二叉树都应该是键值对,如果只有值,查找函数返回值的地址,如果使用地址修改了值,会发生令人不愉快的事情。
typedef struct _HashBucket_
{
_ElementNode_ Data[BUCKET_SIZE];
struct _HashBucket_ *Bucket; //解决冲突的方式是链地址法
}HashBucket; //桶节点类型
typedef HashBucket* HashTable;
bool HashTableInit(HashTable *Hash); //初始化哈希表
bool HashTableInsert(HashTable Hash, KeyType Key, EleType Data); //插入键值对
EleType *HashTableFind(HashTable Hash, KeyType Key); //根据键查找值
bool HashTableDelete(HashTable Hash, KeyType Key); //删除键值对
void HashTableDestroy(HashTable *Hash); //摧毁哈希表
#endif //_HASHTABLE_H_
#include "HashTable.h"
static int HashFunc(KeyType Key)
{
return Key % HASHTABLE_SIZE; //除留取余法
}
static _ElementNode_ *HashTableFind_(HashTable Hash, KeyType Key)
{
int pos = 0, i = 0;
HashBucket *p = NULL;
if( NULL == Hash )
return NULL;
pos = HashFunc(Key); //求Key所对应的基桶
p = Hash + pos; //指向该基桶
for (i = 0; i < BUCKET_SIZE; ++i) //在基桶内寻找位置
{
if( Key == p->Data[i].Key )
return p->Data + i; //返回找到的位置
}
//不在基桶中
while (NULL != p->Bucket) //到溢出桶中查找
{
p = p->Bucket;
for (i = 0; i < BUCKET_SIZE; ++i)
{
if( Key == p->Data[i].Key )
return p->Data + i;
}
}
//也不在溢出桶中
return NULL;
}
//更新一个桶内的元素,如果是以存在元素则用新值更新,如果是不存在元素则找空位置插入
static bool UpdataBucket(HashBucket *p, KeyType Key, EleType Data)
{
int i = 0;
for (i = 0; i < BUCKET_SIZE; ++i) //遍历该基桶中的每个位置,查找是否有空位,或者是否可以更新现有元素
{
if (Key == p->Data[i].Key) //Key已存在则更新Val,相当于是对已存在元素进行修改
{
p->Data[i].Val = Data;
return true;
}
if (INVALID_VALUE == p->Data[i].Key) //Key不存在则增加元素记录,即将新元素存入哈希表
{
p->Data[i].Key = Key;
p->Data[i].Val = Data;
return true;
}
}
return false;
}
//创建一张含有HASHTABLE_SIZE个基桶的哈希表
bool HashTableInit(HashTable *Hash)
{
int i = 0, j = 0;
HashBucket * p = (HashBucket *)malloc(sizeof(HashBucket) * HASHTABLE_SIZE);
if( NULL == p )
{
*Hash = NULL;
return false;
}
for (i = 0; i < HASHTABLE_SIZE; ++i) //初始化全部基桶
{
for (j = 0; j < BUCKET_SIZE; ++j) //初始化桶内每个位置
{
p[i].Data[j].Key = INVALID_VALUE;
p[i].Data[j].Val = INVALID_VALUE;
}
p[i].Bucket = NULL;
}
*Hash = p;
return true;
}
//插入操作函数,当Key已存在时,更新元素,否则在空位置中插入元素
bool HashTableInsert(HashTable Hash, KeyType Key, EleType Data)
{
int pos = 0, i = 0;
HashBucket *p = NULL, *s = NULL;
if( NULL == Hash )
return false;
pos = HashFunc(Key); //求Key所对应的基桶
p = Hash + pos; //指向该基桶
if( true == UpdataBucket(p, Key, Data) ) //桶内元素更新成功
return true;
//基桶内已没有空位置
while (NULL != p->Bucket) //在溢出桶内寻找位置
{
p = p->Bucket;
if (true == UpdataBucket(p, Key, Data)) //桶内元素更新成功
return true;
}
//在已查找过的桶内都未能找到合适的位置,创建一个新的溢出桶来存储元素
s = (HashBucket *)malloc(sizeof(HashBucket));
if( NULL == s )
return false;
for (i = 0; i < BUCKET_SIZE; ++i) //初始化桶
{
s->Data[i].Key = INVALID_VALUE;
s->Data[i].Val = INVALID_VALUE;
}
s->Bucket = NULL; //初始化桶
s->Data[0].Key = Key; //将Key值对存储在溢出桶首位置中
s->Data[0].Val = Data;
p->Bucket = s; //将新桶挂接上去
return true;
}
EleType *HashTableFind(HashTable Hash, KeyType Key)
{
_ElementNode_ *p = HashTableFind_(Hash, Key);
if( NULL != p )
return &(p->Val); //查找到元素,返回元素的值成员地址
return NULL;
}
bool HashTableDelete(HashTable Hash, KeyType Key)
{
_ElementNode_ *p = HashTableFind_(Hash, Key);
if( NULL != p )
{
p->Key = INVALID_VALUE; //查找、插入都是根据Key值来的,因此只需覆盖Key即可
return true;
}
else
return false;
}
void HashTableDestroy(HashTable *Hash)
{
HashBucket *p = NULL, *q = NULL, *s = NULL;
int i = 0;
if( NULL == Hash )
return ;
p = *Hash; //指向首个基桶
for (i = 0; i < HASHTABLE_SIZE; ++i) //释放全部基桶
{
q = p->Bucket; //指向某个基桶的溢出桶所构成的链表
while (NULL != q) //释放溢出桶所构成的链表
{
s = q->Bucket;
p->Bucket = s;
free(q);
q = s;
}
++p;
}
free(*Hash); //释放全部基桶
*Hash = NULL;
}
//main.c
#include "HashTable.h"
#include <stdio.h>
int main(int argc, char **argv)
{
HashTable hash = NULL;
EleType *e = NULL;
// KeyType KeyArray[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// EleType EleArray[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
KeyType KeyArray[] ={ 1, 18, 35, 52, 69, 86, 103, 120, 137, 171, 171, 188, 205, 222, 239, 255, 273 };
EleType EleArray[] ={ 10, 180, 350, 520, 690, 860, 1030, 1200, 1370, 1710, 0, 1880, 2050, 2220, 2390, 2550, 2730 };
int size = sizeof(KeyArray) / sizeof(KeyType);
int i = 0;
bool ret = true;
KeyType key = 0;
HashTableInit(&hash);
for (i = 0; i < size && ret; ++i)
ret = HashTableInsert(hash, KeyArray[i], EleArray[i]);
key = 156;
e = HashTableFind(hash, key);
if( NULL != e )
{
printf("值为%d\n", *e);
*e = 123;
printf("修改后\n");
e = HashTableFind(hash, key);
if (NULL != e)
printf("值为%d\n", *e);
}
else
printf("键%d不存在\n", key);
key = 171;
ret = HashTableDelete(hash, key);
if( true == ret )
{
printf("键值对%d删除成功\n", key);
e = HashTableFind(hash, key);
if (NULL != e)
printf("值为%d\n", *e);
else
printf("键%d不存在\n", key);
}
else
printf("键%d不存在\n", key);
HashTableDestroy(&hash);
return 0;
}