哈希查找(代码实现 注释超详细哦)
什么是理想的哈希表呢。哈希表(通常用数组来存放这张表)中记录的存储位置和他的关键字之间有一个确定的对应关系f(key),这就是哈希函数,常见的哈希函数;
- 直接定制法,除留余数法
它使每一个关键字和结构中唯一一个存储位置相对应。因而查找时可以根据f(key)算出要查找关键字的位置,达到快速查找的目的(不需要比较查找效率可达到O(1))。但是这是一种很理想的情况,实际上在应用中会有这种情况的产生;
-
- 多个元素通过相同的哈希函数——》计算出相同的哈希地址—-〉产生哈希冲突(原位置被占)
- 那么如何解决哈希冲突?
- 1》检查哈希函数设计是否合理?
- 2》哈希函数的值域必须在哈希表的范围内
- 3》尽量使哈希地址分散。(其中一种方法就是二次探测)
当已经发生时;
4》从发生哈希冲突的位置开始,向后找下一个“哈希地址”。(逐个超后探测,线型探测)缺陷;存在数据堆积。防治方法(二次探测)
如下是我实现的代码
#include <malloc.h>
#include <stdio.h>
#include <assert.h>
typedef enum {EX,EM,DL}State;//储存位置的状态,是已存,空,还是删除过,在这个哈希表中删除过元素的存储空间是不可再用的
typedef int DataType;
typedef struct HTELEM {
DataType _data;//存入的关键字(关键字不一定是数值,当然目前我们做的这个表中存的是数值)
State _state;
}HTELEM;
typedef struct HashTable{
HTELEM* arr;//存放哈希表的数组
int _capacity;//数组的容量
int _size;//已用存储位置的大小
int _IsLineDetective;//标志着选择的哈希函数
}HT,HashTable;
void swap (HT*ht,HT*newht)//交换两个哈希表的内容
{
int temp=0;
temp=(int)ht->arr;
ht->arr=newht->arr;
newht->arr=(HTELEM*)temp;
temp=ht->_capacity;
ht->_capacity=newht->_capacity;
newht->_capacity=temp;
temp=ht->_size;
ht->_size=newht->_size;
newht->_size=temp;
temp=ht->_IsLineDetective;
ht->_IsLineDetective=newht->_IsLineDetective;
newht->_IsLineDetective=temp;
}
void HashTableInit(HT*ht ,int capacity,int IsLineDetectiev)//初始化哈希表
{
int i=0;
ht->arr=(HTELEM*)malloc(sizeof(HTELEM)*capacity);//给存放哈希表的数组动态开辟内存
for (i=0;i<capacity;i++)//所有位置的储存状态置为空
{
ht->arr[i]._state=EM;
}
ht->_size=0;
ht->_IsLineDetective=IsLineDetectiev;
ht ->_capacity=capacity;
}
int HashFunc (DataType data, HT*ht)
{
return data%ht->_capacity;//哈希函数;除留余数法
}
int DetectiveLine(int hashaddr,HT*ht)//一次线型探测
{
hashaddr++;
if (hashaddr==ht->_capacity)
hashaddr=0;
return hashaddr;
}
int Detective2(int hashaddr,int i,HT*ht)//二次(平方)探测
{
hashaddr=hashaddr+2*i+1;
if (hashaddr>=ht->_capacity)
hashaddr%=ht->_capacity;
return hashaddr;
}
void HashTableInsert (HT *ht ,DataType data)//哈希表的插入
{
int i=0;
int hashaddr=-1;
assert (ht);
CheckCapacity(ht);//查看并决定是否需要扩容
hashaddr=HashFunc(data,ht);//通过哈希函数计算应插位置
while (EM!=ht->arr[hashaddr]._state)
{
if (ht->arr[hashaddr]._state==EX)
{
if (ht->arr[hashaddr]._data==data)//已经有这个数据就不用插入,直接返回
return;
}
if (ht->_IsLineDetective)//当前的位置被占(被不是要插的数据占),或被删除过,则不可用(哈希冲突),向后探测
{
hashaddr=DetectiveLine(hashaddr,ht);
}
else {
++i;
hashaddr=Detective2(hashaddr,i,ht);
}
}
ht ->arr[hashaddr]._state=EX;//向已经找到的位置,放入数据
ht->arr[hashaddr]._data=data;
ht->_size++;
}
int HashTableFind (HT*ht,DataType data)//在哈希表中查找数据,与插入相似
{
int hashaddr=-1,startaddr=-1,i=0;
assert(ht);
hashaddr =HashFunc(data,ht);
startaddr=hashaddr;//记录开始查找位置(既不发生哈希冲突时应存位置)
while (EM!=ht->arr[hashaddr]._state)
{
if (ht->arr[hashaddr]._state==EX)//找到返回
if (ht->arr[hashaddr]._data==data)
return hashaddr;
if (ht->_IsLineDetective)//向后查找
{
hashaddr=DetectiveLine(hashaddr,ht);
if (hashaddr==startaddr)
return -1;
}
else {
++i;
hashaddr=Detective2(hashaddr,i,ht);
if (hashaddr==startaddr)
return -1;
}
}
return -1;
}
void HashTableDelete(HT* ht ,DataType data)//哈希表的删除
{
int ret=-1;
assert (ht);
ret =HashTableFind(ht,data);
if (ret!=-1)
{
ht->arr[ret]._state=DL;
ht->_size--;
}
}
int HashTableSize(HT*ht)//哈希表的大小
{
assert (ht);
return ht->_size;
}
int HashTableEmpty(HT*ht)//哈希表是否为空
{
assert (ht);
if ( 0==ht->_size)
return 1;
}
void HashTableDestory(HT*ht)//哈希表的销毁
{
int i=0;
free(ht->arr);
ht->arr=NULL;
ht->_size=0;
ht->_IsLineDetective=0;
ht ->_capacity=0;
}
int CheckCapacity(HT*ht)//哈希表的扩容
{
int i=0;
int capacity;
assert (ht );
if (ht->_size*10/ht->_capacity>7)//当已用空间占到总容量的70%时扩容
{
HT newht;
capacity=ht->_capacity*2;
HashTableInit(&newht ,capacity,ht->_IsLineDetective);//初始化一个新表,(新表的容量是老表的2倍)
for (i=0;i<ht->_capacity;i++)//把老表中的元素插入新表
{
if (EX==ht->arr[i]._state)
HashTableInsert (&newht ,ht->arr[i]._data);
}
swap(&newht,ht);//把新表的内容交给老表(新表只是一个函数体内的变量,除了函数体会被销毁)
HashTableDestory(&newht);//销毁新表
}
return 1;
}
void TestHashTable()
{
HashTable ht;
HashTableInit(&ht,6,1);
HashTableInsert (&ht ,23);
HashTableInsert (&ht ,14);
HashTableInsert (&ht ,74);
HashTableInsert (&ht ,33);
HashTableInsert (&ht ,19);
HashTableInsert (&ht ,29);
HashTableInsert (&ht ,29);
printf("size=%d\n",HashTableSize(&ht));
if (-1!=HashTableFind(&ht,33))
{
printf ("have\n");
}
else
printf ("no have\n");
if (-1!=HashTableFind(&ht,3))
{
printf ("have\n");
}
else
printf ("no have\n");
HashTableDelete(&ht,23);
printf ("size=%d\n",HashTableSize(&ht));
if (-1!=HashTableFind(&ht,33))
{
printf ("have\n");
}
else
printf ("no have\n");
}
int main ()
{
TestHashTable();
return 0;
}
-- INSERT -- 258,2 Bot
这里就完成了我们关于哈希表的代码。大家有什么意见或者建议,或者哪里不清楚可以留言呦!