1、哈希表(散列表hash table)定义
哈希表就是利用哈希算法(散列技术)将记录保存到一块连续的存储空间中,这块连续的存储空间就叫做哈希表或散列表。
散列技术就是根据记录的存储位置和它的关键字KEY建立一个确定的对应关系F,使得每一个关键字key对应一个存储位置F(key)。查找时根据这个确定的对应关系找到给定值key的映射F(key)。
这个确定的对应关系就叫做散列函数,又叫做哈希函数。关键字对应的记录存储位置叫做散列地址。
2、哈希表的查找步骤:
(1)、当存储时,通过散列函数计算出散列地址,然后按照散列地址将记录存储到散列表中。
(2)、当查找时,使用与存储时相同的散列函数计算出散列地址,按次散列地址访问该记录。
所以散列技术即使一种存储方法,又是一种查找方法。
3、哈希表与其他数据结构的区别(如线性表、树、图等)?
其他数据结构存储的数据元素之间存在某种逻辑关系,而哈希表的记录之间不存在任何逻辑关系,记录只和关键字存在关联。即一个关键字对应一个记录。哈希表存储的是键值对的形式。
哈希表的好处是查找速度快,查找时较其他数据结构简化了比较过程,提高了效率。但哈希表不具备很多常规数据结构的能力,如当一个关键字对应多个记录时,不适合用哈希表存储;同时哈希表也不适合范围查找,对与对记录排序、获取最大值最小值也不能得到。
散列冲突:当两个不同的关键子key1和key2通过散列函数计算得到相同的散列地址时,这种现象就叫做冲突,引起冲突的关键字称为散列函数的同义词。
4、散列函数的构造方法
设计好的散列函数的原则:计算简单和散列地址分布均匀。
(1)、直接定址法:即取关键字的某个线性函数值作为散列地址。
散列函数公式:f(key) = a*key + b;(a、b为常数)
优点:简单、均匀、不会产生冲突。
缺点:需要事先直到关键字的分布情况,适合查找表小且连续的情况。
(2)、数字分析法:抽取关键字的一部分来计算散列地址的方法。
适合处理关键字位数比较大的情况,当事先知道关键字的分布且关键字的若干位分布均匀,这种方法适合。
(3)、平方取中法:取关键字平方的中间几位作为散列地址。
比较适合事先不知道关键字的分布,而关键字位数不是很大的情况。
(4)、折叠法:将关键字从左到右分成位数相同的几部分(最后一部分位数不够可以短些),然后将这几部分叠加求和,再根据哈希表表长,取后几位作为散列地址。
折叠法事先不知道关键字的分布,适合关键字位数较多的情况。
(5)、除留余数法:此方法是最常用的构造散列函数的方法,对于散列表长m的散列函数公式为:f(key) = key mod p (p<=m)
关键就是选择合适的p值,若表长为m,通常p为小于或等于表长(最好接近m)的最大质数或不包含小于20质因子的合数。
(6)、随机数法:取关键字的随机函数值作为散列地址。
当关键字的长度不等时,采用随机函数法比较合适。
5、处理散列冲突的方法
设计再好的散列函数也不可能完全避免冲突的发生,所以要使用其他的方法来解决冲突。
(1)、开放定址法(闭哈希法):就是一旦发生冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
开放定址法又分为三种方法:
线性探测法:散列函数的公式为f(key) = (f(key) + di) mod m;(di=1、2、3、........m-1)
di为位移量。线性探测法容易产生堆积现象,就是原来不是同义词的两个关键字在冲突计算过程中得到相同的散列地址。
平方探测法:散列函数的公式为f(key) = (f(key) + di) mod m;(di=1^2、-1^2、2^2、-2^2、.........p^2、-p^2)(p<=m/2)。
随机探测法:位移量di采用随机函数计算得到。散列函数的公式为f(key) = (f(key) + di) mod m;(di是一个随机数列)。
(2)、再散列函数法:对于一个散列表来说,事先准备多个散列函数。每当发生散列冲突时,就换一个散列函数计算散列地址,总会有一个可以解决冲突。这种方法使得关键字不产生聚集,相应的也增加了计算时间。
(3)、链地址法(开哈希法):就是将所有关键字为同义词的记录存储到一个单链表中,称次单链表为同义词子表,在散列表中只存储同义词子表的头指针(散列地址)。优点是无论有多少冲突,都是在当前位置给单链表增加节点的问题;缺点:查找时需要遍历单链表。链地址法不会产生任何堆积,因而具有更佳的平均查找性能。
(4)、公共溢出区法:建立一个公共溢出区来存储所有冲突的关键字(同义词)。
当查找时,通过给定的关键字通过散列函数计算出散列地址,先与基本表(散列表)的相应位置进行比较,如果相同,查找成功。不相等,则到溢出表中顺序查找。
6、散列表的查找性能
如果散列表没有发生冲突,散列表的查找性能最高,时间复杂度为O(1)。但是实际应用中,冲突是不可避免的。
散列表的平均查找长度取决那些因素?
1>、散列函数是否均匀
2>、处理冲突的方法:相同的关键字、相同的散列函数,但处理冲突的方法不同,则会使平均查找长度不同。线性探测法处理冲突可能会产生堆积(就是原来不是同义词的两个关键字在冲突计算过程中得到相同的散列地址),显然没有平方探测法性能好,而链地址法处理冲突不会产生任何冲突,所以具有更佳的平均查找性能。
3>、散列表的装填因子:装填因子a=填入表中的记录个数/散列表表长。a标志着散列表的装满程度。当填入表中的记录越多,装填因子越大,产生冲突的可能性越大。所以:散列表的平均查找长度取决于装填因子,而不是取决于查找集合的记录个数。
总结:散列表(哈希表)是一种非常高效的查找数据结构,它与之前数据结构的查找不同,它避免了关键字之间反复的比较。而是通过关键字直接查找到结果。散列表对于那些查找性能要求高,记录之间又没有任何要求的数据有非常好的实用性。
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。
分为两个步骤:首先统计query串的次数,然后在找出top10。
统计query串的次数:遍历所有检索串,采用hash表存储,key存储query串,value存储query串的次数。时间复杂度为O(N)。
找出top10:将哈希表建成小顶堆,然后用堆排序找出top10。