本文转载自:
[1]https://blog.csdn.net/qq_22238021/article/details/78258605
[2]https://blog.csdn.net/duan19920101/article/details/51579136?utm_source=copy

什么是哈希表(散列表)?

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
记录的存储位置 = f(关键字)
这里的对应关系f称为散列函数,又称为哈希(Hash函数),采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。

一、散列函数的构造方法

1、直接定址法

关键码本身和地址之间存在某个线性函数关系时,散列函数取为关键码的线性函数,即:H(key)=a*key+b,a、b均为常数。
散列表(哈希表)(散列函数构造、处理冲突、查找)-LMLPHP
这样的散列函数优点就是简单、均匀,也不会产生冲突,但问题是这需要事先知道关键字的分布情况,适合査找表较小且连续的情况。由于这样的限制,在现实应用中,直接定址法虽然简单,但却并不常用。

2、数字分析法

假设关键码完全已知,且每个关键码都是以某个数r为基数(例以10为基数的十进制数)的值,则关键码中若干位恰能构成分布比较均匀的散列地址空间时,可取关键码的若干位的组合作为散列地址。

3、除留余数法

通过选择适当的正整数p,按计算公式H(K)=K mod p来计算关键码K的散列地址。若关键码个数为n,散列表表长为m(一般m>=n),通常选p为小于或等于表长m的最大素数或不包含小于20的质因子的合数,一般也要求p >= n。这种方法计算最简单,也不需根据全部关键码的分布情况研究如何从中析取数据,最常用。

4、平方取中法

将关键码K平方,取K^2中间几位作为其散列地址H(K)的值。
假如有以下关键字序列{421,423,436},平方之后的结果为{177241,178929,190096},那么可以取{72,89,00}作为Hash地址。

5、折叠法

将关键码从低位到高位(或从高位到低位)分割成位数相等的几段,最后一段可以短些,然后将这些段构成的数值按照某种叠加方法求和。最后,在散列地址范围限制下,取求和结果的最后几位作为关键码的散列函数值。叠加方法:
(1)移位叠加:将各段数值最后一位对齐相加;
(2)间界叠加:从各个数值段的一端到另一端来回折叠后(奇数段位正序,偶数为倒序),以最后一位对齐后相加。

6、随机数法

采用随机函数作为散列函数H(Key)=random(Key),其中random为随机函数。
当关键码长度不等时,采用该方法较恰当。

二、冲突的处理方法

1、开放定址法(建立闭散列表)

开放定址(具体的方法点链接)指散列表的地址对任何记录数据都是开放的,即可存储使用。但散列表长度一旦确定,总的可用地址是有限的。闭散列表表长不小于所需存储的记录数,发生冲突总能找到空的散列地址将其存入。查找时,按照一种固定的顺序检索散列表中的相应项,直到找到一个关键字等于k或找到一个空单元将k插入,故是动态查找结构。

2、拉链法(链地址法、建立开散列表)

将所有散列地址相同的记录存储在同一个单链表中,该单链表为同义词单链表,或同义词子表。该单链表头指针存储在散列表中。散列表就是个指针数组,下标就是由关键码用散列函数计算出的散列地址。初始,指针数组每个元素为空指针,相当于所有单链表头指针为空,以后每扫描到一条记录,按其关键码的散列地址,在相应的单链表中加入含该记录的节点。开散列表容量可很大,仅受内存容量的限制。例:具体的关键字列表为(19,14,23,01,68,20,84,27,55,11,10,79),则哈希函数为H(key)=key MOD 13。则采用除留余数法和链地址法后得到的预想结果应该为:
散列表(哈希表)(散列函数构造、处理冲突、查找)-LMLPHP

三、散列表上的查找

时间复杂度分析
查找成功时的平均查找长度ASLsucc、查找不成功时的平均查找长度ASLunsucc。
一般情况下,处理冲突方法相同的散列表,其查找成功时的平均查找长度依赖于散列表的装填因子(负载因子):a=表中填入的记录数/哈希表长度。
a越小,发生冲突的可能性越小,反之,a越大,表中已填入记录越多,再填记录时,发生冲突的可能性越大,查找时给定值需与之进行比较关键码数目越多。
散列表(哈希表)(散列函数构造、处理冲突、查找)-LMLPHP

10-06 18:04