哈希表,又称为散列,是一种更加快捷的查找技术。我们之前的查找,都是这样一种思路:集合中拿出来一个元素,看看是否与我们要找的相等,如果不等,缩小范围,继续查找。而哈希表是完全另外一种思路:当我知道key值以后,我就可以直接计算出这个元素在集合中的位置,根本不需要一次又一次的查找!是不是很神奇呢?其实一点也不神奇,举一个例子就明白了,假如我的数组A中,第i个元素里面装的key就是i,那么数字3肯定是在第3个位置,数字10肯定是在第10个位置。这不是废话么……哈希表就是利用利用这种基本的思想,建立一个从key到位置的函数,然后进行直接计算查找。
但是实际应用中,和可能出现一种问题:就是多个key被映射成为同一个位置。比如:我想用人的名字来映射人的时候,就会出现两个人的名字汉语拼音相同的情况。那么这种情况又该如何处理你?有两种大的思路:
1.建立一个缓冲区,把凡是拼音重复的人放到缓冲区中。当我通过名字查找人时,出现找的不对,就在缓冲区里找。
2.进行再探测。就是在其他地方查找。探测的方法也可以有很多种。
(1)在找到查找位置的index的index-1,index+1位置查找,index-2,index+2查找,依次类推。这种方法称为线性再探测。
(2)在查找位置index周围随机的查找。称为随机在探测。
(3)再哈希。就是当冲突时,采用另外一种映射方式来查找。
基本思想就是这么多,下面我们看看程序:
#include <stdio.h>
#include <malloc.h>
typedef struct HashTable
{
char* data;
int count;
int size;
}HashTable,*pHashTable;
extern int counter;
bool initHashTable(pHashTable ,int );
void destroyHashTable(pHashTable );
void fillHashTable(pHashTable ,char* , int );
int Hash(char );
bool HashSerach(pHashTable , char , int* );
#include "hash.h"
bool initHashTable(pHashTable pH ,int n)
{
pH->count = 0;
pH->size = n;
pH->data = (char*)malloc(sizeof(char) * n);
//起始值为NULL
for(int i = 0; i < n;++i)
{
pH->data[i] = '*';
}
if(NULL == pH->data)
return false;
else
return true;
}
void destroyHashTable(pHashTable pH)
{
pH->count = 0;
pH->size = 0;
free(pH->data);
pH->data= NULL;
}
//Hash函数就是简单地取模
int Hash(char key)
{
return (int)key % 7;
}
void fillHashTable(pHashTable pH ,char* key, int n)
{
int tmp;
for(int i = 0; i < n; ++i)
{
tmp = Hash(key[i]);
//如果这个位置没有人用过,那么就把键填过去
if('*' == pH->data[tmp])
pH->data[tmp] = key[i];
else
{
//直到找到一个没人用过的位置
while('*' != pH->data[tmp])
{
//跳过7个位置,再次判断有没有被使用
tmp = (tmp + 7) % pH->size;//防止越界
}
//如果没有,则把值填过去
pH->data[tmp] = key[i];
}
++pH->count;
}
}
//使用全局变量来记录运算次数
int counter = 0;
bool HashSerach(pHashTable pH, char key, int* index)
{
int tmp = Hash(key);
//直接找到
++counter;
if(pH->data[tmp] == key)
{
*index = tmp;
return true;
}
else
{
while(true)
{
//增加7个元素,再次尝试
tmp += 7;
++counter;
if(pH->data[tmp] == key)
{
*index = tmp;
return true;
}
//tmp越界,表明没有找到
if(tmp > 30)
{
*index = -1;
return false;
}
}
}
}
最后看看主函数:
#include "hash.h"
int main(void)
{
HashTable h;
initHashTable(&h,30);
char test = 'a';
char arr[26];
for(int i = 0; i< 26;++i)
{
arr[i] = test + i;
}
fillHashTable(&h,arr,26);
for(int i = 0; i < h.size;++i)
printf("%c",h.data[i]);
printf("\n");
int index = 100;
for(int i = 0; i < 26;++i)
{
HashSerach(&h,test+i,&index);
if(index >= 0)
printf("find %c\n",h.data[index]);
else
printf("can't find the char");
}
printf("总的查找次数为%d",counter);
destroyHashTable(&h);
return 0;
}
这个程序中是通过取模来模拟查找到重复元素的过程。对待重复元素的方法就是再哈希:对当前key的位置+7。最后,可以通过全局变量来判断需要查找多少次。我这里通过依次查找26个英文字母的小写计算的出了总的查找次数。显然,当总的查找次数/查找的总元素数越接近1时,哈希表更接近于一一映射的函数,查找的效率更高。