什么是哈希搜索结构
顺序搜索以及二叉搜索树中,元素存储位置和元素各关键码之间没有对应的关系,因此在查找一个元素的时候,必须要经过关键码的多次比较。所以这样的搜索效率取决于搜索过程中元素的比较次数。
而我们理想的搜索方法就是不用经过任何比较,一次直接从表中得到要搜索的元素。如果能够构造一种结构,让所要查找的关键码与它的存储位置有一定的关系,那么可以根据这种关系找到其位置从而更快的找到关键码。
在这种结构中插入时,根据待插入的元素从而经过某些函数计算出它的插入位置,进行插入;查找时通过对查找元素的计算出其位置,再根据位置在结构中的对应出进行比较,如果相等则查找成功。
这种方式的结构就是哈希搜索结构,其中使用的计算位置函数就是哈希函数。
哈希冲突
在说哈希冲突之前,首先我们先定义一个简单的哈希函数方便我们解释。
int HashFun(int key)
{
return key % 1000;//关键码为key,其存储对应位置为key模1000
}
这时候我们就可以建立起关键码与其存储位置之间的关系了。
这个时候的1001应该插入到什么位置呢?这种情况的出现就是哈希冲突。
解决哈希冲突常见的有两种方法:闭散列和开散列。
负载因子
为了增加哈希表的搜索效率,尽量不让哈希表插满,而是规定一个负载因子,控制哈希表内插入元素的个数,从而提高效率。
负载因子的定义为:α = 填入表中元素 / 可填入总元素
α 越大,表明填入表内元素越多,产生哈希冲突的可能就越大。相反则越小。
闭散列
闭散列解决哈希冲突的方式是,当发生哈希冲突时,如果哈希表未放满,则哈希表内必定有空余位置,那么此时将冲突的关键码放置表中的下一个空位去。
接下来来,我们实现一下闭散列哈希表的基本操作。
实现
//hash.h
#pragma once
#include <stddef.h>
#define HASHMAXSIZE 1000//总个数
typedef int Keytype;
typedef int Valtype;
typedef size_t (*HashFun)(Keytype key);
typedef enum Stat {
Empty,//空
Valid,//有效
Delete//已删除
} Stat;//标志位
typedef struct HashElem {
Keytype key;//关键码
Valtype val;//data
Stat stat;
} HashElem;
typedef struct HashTable {
HashElem data[HASHMAXSIZE];
size_t size;//元素个数
HashFun fun;//哈希函数
} HashTable;
void HashInit(HashTable* ht, HashFun fun);//初始化哈希表
void HashInsert(HashTable* ht, Keytype key, Valtype val);//插入哈希表
void HashDestroy(HashTable* ht);//销毁哈希表
void HashRemove(HashTable* ht, Keytype key);//删除哈希表指定元素
int HashFind(HashTable* ht, Keytype key, Valtype* val);//查找哈希表内元素
//hash.c
#include "hash.h"
#include <stdio.h>
size_t Function(Keytype key) {
return key % HASHMAXSIZE;
}
void HashInit(HashTable* ht, HashFun fun)//初始化哈希表
{
if(ht == NULL) {
return;
}
ht->size = 0;
ht->fun = fun;
size_t i = 0;
for(; i < HASHMAXSIZE; ++i) {
ht->data[i].stat = Empty;
}
return;
}
void HashInsert(HashTable* ht, Keytype key, Valtype val)//插入哈希表
{
if(ht == NULL) {
return;
}
if(ht->size > 0.8 * HASHMAXSIZE) {//负载因子
return;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].key == key && ht->data[offset].stat == Valid) {
//如果关键值key相同,那么就插入失败
return;
}
if(ht->data[offset].stat != Valid) {
ht->data[offset].key = key;
ht->data[offset].val = val;
ht->data[offset].stat = Valid;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
++ht->size;
return;
}
void HashDestroy(HashTable* ht)//销毁哈希表
{
if(ht == NULL) {
return;
}
ht->size = 0;
ht->fun = NULL;
size_t i = 0;
for(; i < HASHMAXSIZE; ++i) {
ht->data[i].stat = Empty;
}
return;
}
void HashRemove(HashTable* ht, Keytype key)//删除哈希表指定元素
{
if(ht == NULL) {
return;
}
if(ht->size == 0) {
return;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].stat != Valid) {
break;
}
if(ht->data[offset].key == key) {
ht->data[offset].stat = Delete;//改变状态
--ht->size;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
return;
}
int HashFind(HashTable* ht, Keytype key, Valtype* val)//查找哈希表内元素
{
if(ht == NULL) {
return 0;
}
if(ht->size == 0) {
return 0;
}
size_t offset = ht->fun(key);
while(1) {
if(ht->data[offset].stat != Valid) {
return 0;
}
if(ht->data[offset].key == key) {
ht->data[offset].stat = Delete;
--ht->size;
break;
}
++offset;
if(offset >= HASHMAXSIZE) {
offset = 0;
}
}
*val = ht->data[offset].val;
return 1;
}
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!