1.哈希表
2.哈希函数
3.哈希冲突
哈希表
哈希表是一种按key-value存储的数据结构,也称散列表。
**之前的数组、树和图等等查找一个值时都要与结构中的值相比较,查找的效率取决于比较的次数。
**
而哈希表因为key与value对应,则可以在较少的比较次数中找到元素。
哈希函数
哈希表是对于给定的一个key,建立一个函数H(key),y =H(key)即为元素key在数组内的储存位置。
1.直接定址法
**直接建立一个与key相关的线性函数形如:H(key)=k*key+b
**
**2.数字分析法
**
就是找找规律,尽量让大多数H(key)落在不同区间,例如对于身份证号码,前面的数字重复率太高,可以考虑用出生年月日计算当key。
**3.平方取中法
**
取关键字平方后的中间几位当做哈希地址。
4.折叠法
将关键字分成位数相同的几个部分(最后一部分位数可以不同),然后取这几部分的叠加和作为哈希地址。
关键字位数很多,并且数字分步均匀时可以考虑采用折叠法。
5.除留余数法
形如H(key) = key %m.
6.随机数法(还莫得理解)
哈希冲突
当key过多时,就容易发生多个key对于H出现同样的结果,就是它们会占用数组同一个位置,这就是哈希冲突。
冲突是不可避免的,我们要做的就是要尽可能的使冲突达到最小。
常用的处理冲突的方法有:
1.开放定址法
这个时候H* = (H(key)+di)%m , di = 1、2、3……..m-1
这里对于di 的值又有三种取法:
1)线性探测再散列:
顾名思义,di取值为1,2,3…..m-1,也就是当前位置已经有元素时,会像下一个位置走。
2)二次探测再散列
这里di 取值为 1^2 、 -1^2 、2^2、-2^2…….- (m/2)^2 , m为散列表长度,若是算出key+di为负数,则加上m再取余,也就是移到了后面,即-1就移到最后一位。
3)伪随机探测再散列(这个还没搞懂)
2.链地址法
链地址法,看图理解。
经过哈希函数后,对于处于同一位置的元素,用链表将它们储存。
(图源自网络)
3.再哈希法
Hi = RHi(key), i = 1,2,3…..k
RHi 是不同的函数,在用一个哈希函数产生冲突时就调用另一个,直到不产生冲突为止。
4.建立公共溢出表
也就是建立另一个区间,产生冲突的都放进另一个区间。感觉不太好用,冲突多了比较次数又多了。
下面用除余法和链地址法(尾接法)写的代码如下:
#include
using namespace std;
#define hashsize 11
struct Node{//链表节点
int data;
Node *next;
int flag;
Node(){
flag=;
next=NULL;
}
Node (int num){
flag=;
data=num;
next=NULL;
}
};
class Hash{
private:
Node *hashtable;
int len;//记录现在数据个数
public:
Hash(){
len=;
hashtable = new Node\[hashsize\];
}
~Hash(){
}
//获得key值 ,哈希函数
int getKey(int num){
return num%hashsize;
}
//往表中插入元素 ,链接法
void insertData(int num){
int key = getKey(num);//获得key值
if(hashtable\[key\].flag==){//此时当前key位置无数据
hashtable\[key\].data=num;
hashtable\[key\].flag=;
}
else{//尾插入法
Node \*tmp = &hashtable\[key\];//指针要获得地址才能赋值
while(tmp->next!=NULL)
tmp=tmp->next;
tmp->next = new Node(num);
}
len++;
}
//查找hash表中是否有这个元素,有则返回查找次数,无则返回-1
int find(int num){
int sum=;
int key = getKey(num);//获取该数键值
if(hashtable\[key\].flag==){
insertData(num);
return -;
}
else{
Node \*tmp = &hashtable\[key\];
while(tmp->data!=num){
sum++;
if(tmp->next==NULL){
insertData(num);
return -;
}
tmp=tmp->next;
}
//能退出上面的while说明找到相等的了,return sum
return sum;
}
}
};
int main(){
int n;
cin>>n;
int num[];
for(int i=;i
Hash \*hash = new Hash();
for(int i=;i<n;i++)
hash->insertData(num\[i\]);
cin>>n;
for(int i=;i<n;i++){
cin>>num\[i\];
if(hash->find(num\[i\])!=-)
cout<<hash->getKey(num\[i\])<<" "<<hash->find(num\[i\])<<endl;
else cout<<"error"<<endl;
}
这里待测样例如下:
样例输出:
接下来是用除余法与二次散列再分配写的代码:
//该程序使用二次散列再查找的方式处理冲突构建哈希表
//哈希函数选择除于11的方法
#include
using namespace std;
#define modnum 11
struct HashData{
int data;
int flag;
HashData(){
flag=;//当查找次数
}
};
class Hash{
private:
HashData \*hashtable;//哈希表
int len;
int \*value;
public:
Hash(int n){
hashtable = new HashData\[n\];
len=n;
int flag = -;
int pos=;
value = new int \[len\];
for(int i=;i<len/;i++){
value\[pos++\]=i\*i;
value\[pos++\]=flag\*i\*i;
}
}
~Hash(){
}
int getKey(int num){
return num%modnum;
}
void insert(int num){
int key = getKey(num);
if(hashtable\[key\].flag==){
hashtable\[key\].data = num;
hashtable\[key\].flag = ;
}
else{
int flag1=;
for(int i=;i<len;i++){
//二次散列 ,找到空的位置
flag1++;
//如果此时key+value\[i\] 是负数就去到H()+len的位置
if(hashtable\[(key+value\[i\]+len)%len\].flag==){
hashtable\[(key+value\[i\]+len)%len\].data = num;
hashtable\[(key+value\[i\]+len)%len\].flag = flag1;
return ;
}
}
}
}
int find(int num){
int key = getKey(num);//获得键值
if(hashtable\[key\].data==num){
return key+;
}
else{
for(int i=;i<len;i++){
if(hashtable\[(key+value\[i\]+len)%len\].data==num){
return (key+value\[i\]+len)%len+;
}
}
return -;
}
}
int getSum(int num){
int key = find(num);//获得键值
if(key != -)
return hashtable\[key-\].flag;
else{
int sum=;
for(int i=;i<len;i++){
sum++;
//说明找不到这个元素
if(hashtable\[(key+value\[i\]+len)%len\].flag==)
break;
}
return sum;
}
}
void show(){
for(int i=;i<len-;i++){
if(hashtable\[i\].flag!=){
cout<<hashtable\[i\].data<<" ";
}
else
cout<<"NULL ";
}
if(hashtable\[len-\].flag!=){
cout<<hashtable\[len-\].data<<endl;
}
else
cout<<"NULL"<<endl;
for(int i=;i<len;i++)
cout<<hashtable\[i\].flag<<" ";
cout<<endl;
}
void deleteData(int num){
int key = find(num);
if(key!=-){
if(hashtable\[key-\].flag!=){
hashtable\[key-\].flag=;
}
}
}
};
int main(){
int n,m;
cin>>m>>n;
Hash *hash = new Hash(m);
int a[];
for(int i=;i
hash->insert(a[i]);
}
hash->show();
//查找
cin>>n;
for(int i=;i
if(hash->find(a[i])!=-){
cout<<<<" "<
for(int i=;i
hash->deleteData(a[i]);
hash->show();
}
}
样例输入(不包括新加的删除样例):
样例输出:
哈希的就先到这里,那些高端操作拜拜了您下次再看。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章