前言:
这是查找这个知识点的最后一个部分,个人感觉这部分真的很好理解,就简单的逻辑思维,不需要绕弯,基本上就可以解决大部分的题目。合理的应用查找的总体效率也是很高的。主要说三个模块的事情:构造方法,处理冲突方法,查找。
那么引入一下,前面我们说了顺序表的查找、树表的查找,但是我们发现,就算再优化,它们的时间复杂度最小也是log2n级别的。那么我们肯定是希望能找到更优的算法,最好就是n(1)级别的,如果能利用公式直接的求解,那么不是直接就可以找到了,不需要依次比较了吗?所以就有了我们要说的散列表的查找(也叫哈希表)。
一、构造方法
来看看基本的概念:
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
构造方法有很多,数字分析法、平方取中法、折叠法、除留余数法(这个是最常用的)。只需要思考一下就知道我们构造其实就是制定一个规则,使我们的元素可以按照这个规则查找到,当然,我们还可以有其他的方法(例如:你自己定义一个高等数学的计算公式,结构不一样,依旧可以映射到散列地址上)。
1、除留余数法
这个是最常用的,理解也很简单。假设散列表表长为m,选择一个不大于m的数p(这里我们可以思考,最好的p应该是小于等于m的质数),用p去除关键字,除后得到余数为散列地址,即
H(key) = key%p
二、处理冲突的方法
聪明的你会发现,用上述方法肯定会出现冲突,什么是冲突呢,就是我们两个元素根据散列函数计算得到的散列地址是一样的,这个时候怎么办呢?那么肯定需要一个统一的原则去处理这样的冲突,于是我们就有了:开放地址法和链地址法去处理。
1、开放地址法:
基本思想就是冲突的时候以这个冲突元素的地址再求下一个地址,以此类推,知道不冲突为止
1)线性探测法:
这个是最常用的,很好理解,就是当发生冲突的时候,我们都统一向前加一(或者向后加一),知道不冲突结束。
2)二次探测法:
其实和上述的一样,但是加减的规则边了,这里的加减步长不再是线性探测法的±1,而是d=12,-12,22,-22,…
3)伪随机探测法:
这个也很好理解,就是随便一个随机值(例如9,而我们之前的p=11),当我们冲突的时候,它的计算方式就是(9+冲突地址)%11,得到新的地址。
说到这里,你一定有很多其他的解决冲突的方式(现实应用的时候其实我们规定了很多的限制去减少这样的冲突)。
2、链地址法
链地址法的基本思想是:把具有相同散列地址的记录放在同一个单列表中,成为同义词链表。这样我们冲突的时候,不就直接往链表查找就可以了吗?
三、查找
既然你理解了上面的两个部分,那么查找对你来说也一定很容易
【算法描述】
1.给待查找的关键字key,根据造表时设定的散列函数计算H0=H(key);
2.若单元H0为空,则所查找元素不存在;
3.若单元H0中元素的关键字为key,则查找成功;
4.否则重复解决冲突的过程(这个方法很多,给出的代码演示里面使用的冲突解决方法是线性探测法)。
代码演示:
#include<iostream>
using namespace std;
//算法7.10 哈希表的查找
//- - - - -开放地址法哈希表的存储表示- - - - -
#define m 16 //哈希表的表长
#define NULLKEY 0 //单元为空的标记
struct HashTable{
int key; //关键字项
// InfoType otherinfo; //其他数据项
};
// 算法7.10为哈希表查找的算法,采用线性探测法处理冲突。
// 【算法实现】
int H(int key)
{
int result;
result=key%13;
return result;
}
int SearchHash(HashTable HT[],int key){
//在哈希表HT中查找关键字为key的元素,若查找成功,返回哈希表的单元标号,否则返回-1
int H0=H(key); //根据哈希函数H(key)计算哈希地址
int Hi;
if (HT[H0].key==NULLKEY) return -1; //若单元H0为空,则所查元素不存在
else if (HT[H0].key==key) return H0; //若单元H0中元素的关键字为key,则查找成功
else{
for(int i=1;i<m;++i){
Hi=(H0+i)%m; //按照线性探测法计算下一个哈希地址Hi
if (HT[Hi].key==NULLKEY) return -1; //若单元Hi为空,则所查元素不存在
else if (HT[Hi].key==key) return Hi; //若单元Hi中元素的关键字为key,则查找成功
}//for
return -1;
}//else
}//SearchHash
void main()
{
int result;
int a[16]={-1,14,1,68,27,55,19,20,84,79,23,11,10,-1.-1.-1};
HashTable HT[m];
for(int i=0;i<16;i++)
{
HT[i].key=a[i];
}
result=SearchHash(HT,55);
if(result!=-1)
{
cout<<"在第"<<result<<"位置找到"<<endl;
}
else
{
cout<<"未找到"<<endl;
}
}
后记:
*查找部分就完成了,这一系列的操作下来我们其实都是在逐步的找最优的算法,时间最优亦或空间最优。那么下一次应该就是和查找关联的排序了。*有误可以评论指出哦,谢谢。
来源:CSDN
作者:threecat.up
链接:https://blog.csdn.net/qq_43919400/article/details/103486050