哈希这个玩意也是非常经常的出现在广大计算机科学领域的人眼中了。很早很早就接触了,一直没做个总结,现在就写一篇博客总结一下。
散列表(hash table)主要用来查找,不做排序用。哈希查找很好,O(1)即可查找到想要的值。
本身想要快速查找到某一个元素,一是可以另所有元素严格有序,来快速查找,二分查找就是个典型的例子;二是可以令元素部分有序,二叉排序树或者也叫二叉查找树是个典型的例子;三就是哈希一样直接设计函数。
储存方法:
哈希的存数据和取数据的方法是一样的,怎么存就怎么取。存的方法是给一个f(x)做x的映射得到存储下标,简单例子:你有一个数组1,2,3,4,5,然后你用f(x)=x%5这个函数映射,那么你每个元素做一遍%5运算就得到它存储的下标了,存进去即可。你要查找时,也进行相同的运算,比如你想找4这个元素,直接%5,f(x)告诉你4这个元素在哪。
f(x)选择的种类超多,举几个常用例子即可,设计f(x)的核心目的是让算出的存储下标均匀少冲突,所以尽量让数值的每一位都影响到地址。
数值类关键字可以有:
1.直接定地址();
2.除留余数法();
3.数字分析法(给予数字意义、截取字符串变数字等等);
4.折叠法(折叠截取字符串数字然后处理得到数字);
5.平方取中法(平方此数,取中间几位)
字符类关键字可以有:
1.ASCII码加和求余
冲突处理
显然你设计的f(x)是会有冲突的,例子数组A=1,2,3,4,5,6,不巧你设计了,那么在储存1和6的时候就“冲突”了。显然你能想出巨多方法来处理这个问题,我只是做个总结列举经典。
总的来说,冲突了就换地方,怎么换地方看可以分类。数学公式表示出来就是,其中d就是加上的距离,d的取值不同,叫的方法就不同。显然有时候换一次地方是不够的,所以是f(key)i。
1.线性探测。不是这个位置有人占了吗?那我向后移一个位置不就行了?如果后一个位置又被占了,那我再移一个位置就是了。用数学公式来说,就是你第i个要试探的位置是。
2.平方探测。向后或向前移动次位置。先向后找
,再向前找
,数学公式写出来
.而且相关经验和定理表明,如果散列表长度是满足4k+3的素数时,平方探测就能探测到整个表的范围。
3.双散列。简单说不再是一个简单的
或者i了,是个更麻烦些的函数。经验表明取
,其中
效果好些。注意哦,此时是f2取这个值哦,整个式子是这样的
还有一种不同于上述的方法就是地址链接法,简单说就是在同一个位置给一个链表,存放下标地址相同的元素。弊端和优点都显而易见,当链表生的太长,哈希直接退化成链表上的搜索;优点就是不必找来找去,直接在此插入即可。
平均查找时间
简写ASL,他分两种,成功平均查找时间(ASLs)和不成功(ASLu)平均查找时间。如果你看看线性表的查找方式,你就发现“查不到”这个操作不是那么直接的。
装填因子α=填满单位/哈希表长度。
ASL的具体计算方法是:
ASL的平均值是:
1.线性探测
2.平方探测和双散列
由图我们看出α在0.5-0.85之间都是忍受范围。装填太少则空间利用不足,太多则查找效率不高。
删除方法
不用地址链接法的哈希表可不是直接删掉哦!在实现的时候,要删除一个元素是打上delete标记,直接删除会搜索的时候出现错误。那再次插入的时候看见delete的标记可以直接覆盖掉插入即可。
如果用地址链接法,那么直接删除就可以了。