三、哈希查找,也称为散列查找
哈希技术是在记录的存储位置和记录的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。查找时,根据这个确定的对应关系找到给定值的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。哈希技术既是一种存储方法,也是一种查找方法。
六种哈希函数的构造方法:
1,直接定址法:
函数公式:f(key)=a*key+b (a,b为常数)
这种方法的优点是:简单,均匀,不会产生冲突。但是需要事先知道关键字的分布情况,适合查找表较小并且连续的情况。
2,数字分析法:
这种蛮有意思,比如有一组value1=112233,value2=112633,value3=119033, 针对这样的数我们分析数中间两个数比较波动,其他数不变。那么我们取key的值就可以是key1=22,key2=26,key3=90。
3,平方取中法:
故名思义,比如关键字是1234,那么它的平方就是1522756,再抽取中间的3位就是227作为哈希地址。
4,折叠法:
折叠法是将关键字从左到右分割成位数相等的几个部分(最后一部分位数不够可以短些),然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。
比如我们的关键字是9876543210,哈希表表长三位,我们将它分为四组,987|654|321|0 ,然后将它们叠加求和987+654+321+0=1962,再求后3位即得到哈希地址为962,哈哈,是不是很有意思。
5,除留余数法:
函数公式:f(key)=key mod p (p<=m)m为哈希表表长。
这种方法是最常用的哈希函数构造方法。
6,随机数法:
函数公式:f(key)= random(key)。
这里random是随机函数,当关键字的长度不等是,采用这种方法比较合适。
两种哈希函数冲突解决方法:
我们设计得最好的哈希函数也不可能完全避免冲突,当我们在使用哈希函数后发现两个关键字key1!=key2,但是却有f(key1)=f(key2),即发生冲突。
方法一:开放定址法:
开放定址法就是一旦发生了冲突,就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总是能找到,然后将记录插入。这种方法是最常用的解决冲突的方法。
方法二:链地址法:
将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。
实现一:
设计函数采用:”除法取余法“。
冲突方面采用:”开放地址线性探测法"。
#include <iostream>
using namespace std;
void InsertHash(int hash_table[],int len,int data)
{
int key=data%len;
if (hash_table[key]==0)
{
hash_table[key]=data;
}
else
{
int i;
bool flag=false;
for (i=1;i<len;i++)
{
int key2=(key+i)%len;
if (hash_table[key2]==0)
{
hash_table[key2]=data;
flag=true;
i=len;//终止循环
}
}
if (flag==false)
{
throw "Insert failed !!!!";
}
}
}
int HashSearch(int hash_table[],int len,int data)
{
int key=data%len;
if (hash_table[key]==0)
{
return 0;
}
else if (hash_table[key]!=data)
{
int i;
bool flag=false;
for (i=1;i<len;i++)
{
int key2=(key+i)%len;
if (hash_table[key2]==data)
{
flag=true;
i=len;//终止循环
return key2;
}
}
if (flag=false)
{
return 0;
}
}
}
int main()
{
int A[10]={13, 29, 27, 28, 26, 30, 38, 54 , 68 , 27 };
int Hashtable[13];//哈希表长为素数
for (int i=0;i<13;i++)
{
Hashtable[i]=0;
}
for (int i=0;i<10;i++)
{
try
{
InsertHash(Hashtable,13,A[i]);
}
catch (char* )
{
cerr<<"Insert failed !!!!"<<endl;
exit(1);
}
}
for (int i=0;i<13;i++)
{
cout<<Hashtable[i]<<" ";
}
cout<<endl;
int data;
while (1)
{
cout<<"请输入要查找的数据: "<<endl;
cin>>data;
int result=HashSearch(Hashtable,13,data);
if(result!=0)
cout<<"Find the key: "<<result<<endl;
else
cout<<"Can't Find the key! "<<endl;
}
system("pause");
return 0;
}
实现二:
设计函数采用:”除法取余法“。
冲突方面采用:”链地址法"。
#include <iostream>
using namespace std;
typedef struct HashNode{
int data;
HashNode * next;
}HashTable;
void InsertHash(HashTable ** hashtable,int len,int data)//len为 哈希表长度
{
int key=data%len;
if (hashtable[key]->next==NULL)
{
HashTable *p=new HashTable;
p->data=data;
p->next=NULL;
hashtable[key]->next=p;
}
else
{
HashTable *p=new HashTable;
p->data=data;
p->next=NULL;
HashTable *q=hashtable[key];
while(q->next!=NULL&&q->next->data<data)
{
q=q->next;
}
p->next=q->next;
q->next=p;
}
}
int HashSearch(HashTable **hashtable,int len,int data)//len为哈希表长度
{
int key=data%len;
if (hashtable[key]->next==NULL)
{
return -1;
}
else if (hashtable[key]->next!=NULL)
{
HashTable *q=hashtable[key]->next;
while (q->next!=NULL&&q->data!=data)
{
q=q->next;
}
if (q->data==data)
{
return key;
}else
{
return -1;
}
}
}
int main()
{
int A[10]={13, 29, 27, 28, 26, 30, 38, 54 , 68 , 27};
int m=13;//哈希表长为素数
HashTable ** hashtable=new HashTable*[m];
for (int i=0;i<m;i++)
{
hashtable[i]=new HashTable;
hashtable[i]->data=i;
hashtable[i]->next=NULL;
}
for (int i=0;i<10;i++)
{
InsertHash(hashtable,m,A[i]);
}
int data;
while (1)
{
cout<<"请输入要查找的数据: "<<endl;
cin>>data;
int result=HashSearch(hashtable,m,data);
if(result!=-1)
cout<<"Find the key: "<<result<<endl;
else
cout<<"Can't Find the key! "<<endl;
}
system("pause");
return 0;
}