Hash
什么是哈希?
Hash就是把任意长度的输入,通过散列算法,变成固定长度的输出,该输出就是散列值。
哈希表?
利用hash技术实现,理想情况下为一个包含有关键字(key-index,可以为整数,字符串等)的具有固定大小的数组。输入待查找的值即关键字,即可查找其对应的数据元素。
比如 hashMap
Hash函数
最常见(计算简单,适用范围广):
除留余数法
hash(K)=K mod C
K:数据元素关键字
C:接近或等于哈希表本身的长度,选取素数时效果最好其他函数
适用于特定场景,几乎很少见,基本上大同小异,都是为了计算出特定场景下合适的哈希地址。
1、直接寻址法:hash(k)=aK+C
优点:不产生冲突
缺点:空间复杂度高,可能会造成大量的空间浪费
场景:元素较少
2、数字分析法:选取数据元素关键字中某些取值较均匀的数字来作为哈希地址
优点:能尽量避免冲突
缺点:只能适用于能够预先估计出全部关键字上每一位各种数字出现的频率。
3、平方取中法:对关键字进行平方,再取中间的数位数字作为哈希地址。
优缺点不详,平方是否需要注意位溢出?另外运算量是否很大?
4、折叠法:将关键字分割成相同位数的几部分,再将这几部分相加取和,舍去进位后作为哈希地址。
场景:适用于关键字位数较多
5、随机数法:H(key)=Rand(key)
场景:适用于对长度不等的关键字构造哈希函数。
6、旋转法:将关键字的数值位置进行旋转(比如将后两位数字移动前面)
场景:适用于关键字中部分数字相同的情况。
7、相乘取整法:将关键字与某个常数相乘(0
处理哈希碰撞(常见的两种方法)
开放地址法
共同点:通过一系列的计算在哈希表上找到空单元,把元素填进去。
不同点:寻找空单元的计算方法不同
1.线性探测法
冲突发生时,顺序查看表中下一单元,直到找到一个空单元或查遍全表。
2.二次探测法
冲突发生时,在表的左右进行跳跃式探测,比较灵活。
Di=1^2 , -1^2 , 2^2 , -2^2 …..k^2 , -k^2 ( k <= m/2)
3.伪随机探测法
Di = 伪随机数列链地址法(拉链法)
将所有哈希地址为i的元素构成一个称为同义词的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。
Java 的hashMap就是采用链地址法。
特点:寻址困难,插入和删除简单
适用场景:适用了经常插入和删除
比较(开放地址法和链地址法)
1、链地址法处理冲突简单,无堆积现象,非同义词不会发生冲突,因此平均查找长度较短。
2、由于链地址中链表上的结点空间是动态申请的,更适用于无法确定表长的情况。
3、开方地址法为减少冲突,要求装载因子a较小,故当结点规模较大时会浪费很多空间。而拉链法中可先取a>=1,且结点较大时,拉链法中增加的指针域可忽略不计,节省节点。
4、拉链法中删除结点的操作易于实现。只需要简单的删去链表上的节点,但开方地址法中不能简单的删除结点空间置为空,否则将截断在它之后走入散列表的同义词结点的查找路径。这是因为开放地址法中,空地址单元(即开放地址)都是查找失败的条件。因为在开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。实际操作待表格重新整理时在进行——称为情性删除。