哈希第一弹:哈希的概念、哈希函数以及哈希冲突的解决方法
哈希的概念:
哈希表查找的基本思想是:根据当前带查找数据的特征,以记录关键字为自变量,设计一个哈希函数,以该函数按关键码计算该元素的存储位置,并按此存放;查找时,有同一个函数对给定值key计算地址,将key与地址单元中元素关键码进行比较,确定查找是否成功。哈希方法中使用的转换函数称为哈希函数,按这个思想构造的表称为哈希表(散列表)
从上面的概念,构建哈希表关键是有哈希函数。那么我们来看看常见的哈希函数有哪些?
常见哈希函数:
直接定制法:
- 取关键字的某线性函数为哈希地址:Hash(Key) = A*Key +B ;
eg: 4 6 7 8 9 Hash(Key) =Key
4 | 6 | 7 | 8 | 9 |
0 1 2 3 4 5 6 7 8 9
直接定制法,比较好的一点就是简单,直接映射; 不过也有缺点:映射的数据集合应该是分布集中,连续的
eg:1 100000000000
如果只映射两个数1和一个超大的数,直接映射,我们需要开很大的空间,并且空间严重浪费,这就是直接定制法的巨大缺陷。
除留余数法:
- 假设散列长度为m,取一个不大于m但最接近或者等于m的质数p作为除数
- 按照哈希函数 Hash(key) =key%p,将关键码转换成哈希地址
除留余数法,很大程度解决了直接定制法的空间浪费问题。
不常用的哈希函数
平方取中法
- 先计算关键字的平方,再抽取中间的几个数作为哈希地址
eg :
关键字 | 平方 | 哈希地址(抽取了中间3位) |
1234 | 1522765 | 227 |
4321 | 18671041 | 710(671也可以) |
用平方取中法,关键字的位数不能太大。太大的话,最早的情况,平方之后如果超过long long,很难操作
折叠法
- 将关键字从左到右分隔成位数相等的几部分(最后一部分位数可以短一些)
- 再将这几部分叠加求和,并按散列表表长,取后几位最为散列地址
折叠法可以适用于关键字位数较大的情况
随机数法:
- 选择一个随机函数,取关键字的随机函数值为哈希地址;
- 即Hash(key)=random(key)
- 通常应用于关键字长度不等时采用这种方法
数字分析法:
- 如果有n个d位数,每一位上可能由r中不同的符号,这种r中不同的符号在各个位上出现的概率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀,只有某几种符号常见。
- 我们就可以根据散列表的大小,选择其中各个符号分布均匀的若干位作为散列地址。
- 通常用于关键字位数表加大,并且实现直到关键字的若干位分布均匀的情况
总之,设计哈希函数的目的,一是为了映射到哈希表上,二是为了减少产生哈希冲突的可能性(无法完全避免哈希冲突)
可能刚才提到哈希冲突,大家有点迷惑什么是哈希冲突?
接下来,我们就讨论一下哈希冲突及其解决方法。我们主要讨论的哈希函数为直接定制法和除余留数法。
哈希冲突
哈希的概念简单的说就是将关键字映射到表(哈希表)上的某个位置,查询的时候,到这个位置上去找;
那哈希冲突呢? 冲突,就是我们常说的矛盾。即多个关键字,经过哈希函数计算,映射到相同的位置,此时就有了矛盾,这个位置该存哪个关键字呢!这也就是我们说的哈希冲突
以我们上文用到的除余留数法为例,理解哈希冲突
上面我们也提到了哈希冲突是不可避免的(当然,直接定制法,进行一 一映射的情况,可以视为特殊),所以我们需要解决方法:
哈希冲突的解决方法
闭散列(开放定制法):
当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把关键字存放到冲突位置中的“下一个”空位置中去。
如何寻找下一个空位置呢?
线性探测:
从发生冲突的位置开始,依次向后探测,直到寻找下一个空位置为止
二次探测:
从发生冲突的位置开始,找下一个空位置,查找空位置的方法为 (
+i^2)%m,i=1,2,3........;m是哈希表大小,
是经过关键字计算后的出的哈希地址
开散列(链地址法、开链法)
对关键字集合用散列函数计算散列地址,具有相同地址的关键码归于同一个子集合,每一个子集和称为一个桶,各个桶中的元素通过一个单链表连接起来,各链表的头结点存储在哈希表中。
举例理解上面三种解决哈希冲突的方法
线性探测:
从4这个位置开始查找下一个空位置,到5这个位置的时候,发现没有关键字存放,则把444放在5这个位置,倘若又要插入4444,则又要从4这个位置查找下一个空位置,发现6这个位置没有关键字存放,则把4444放到6这个位置;
看图解:
二次探测:
当插入444时,i=1,经过计算,判断5的位置是否有关键字存放,发现没有,则将444存放在5这个位置
当在插入4444时,i=2,经过计算,判断8的位置是否有关键字存放,发现没有,则将4444存放在8这个位置
开链法:
桶结构,将相同关键字存放在一个桶上。
注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!