何为一个工业级的散列表?
1)支持快速的查询、插入、删除操作。
2)内存占用合理,不能浪费过多的内存空间。
3)性能稳定,极端情况下,散列表的性能也不会退化到无法接受的情况。
如何设计散列函数?
首先,散列函数的设计不能太复杂。过于太复杂的散列函数,势必会消耗很多计算时间,也就间接的影响到散列表的性能。其次,散列函数生成的值要尽可能随即并且均匀分布,这样才能避免或者最小化散列冲突,即使出现冲突,散列到每个槽里的数据也会比较平均,不会出现某个槽内数据特别多的情况。
实际工作中还要综合考虑各种因素,比如关键字的长度、分布、特点、散列表的大小……
1 数据分析法:分析数据结构,取比较随机的数据部分作为散列值。
2 直接寻址法:以数据元素关键字本身或它的线性函数作为散列函数。
3 平方取中法:先取关键字的平方,然后根据可使用空间的大小,选取平方数是中间几位为散列值。
4 折叠法:将关键字分割成位数相同的几部分,然后取这几部分地叠加和。
5 随机数法:步长采用随机函数计算得到。
装载因子过大了怎么办?
对散列表动态扩容,重新申请一个更大的散列表,将数据搬移到新散列表中。
装载因子的阈值需要选择得当。大了会导致冲突过多,小了会导致内存浪费严重。
要权衡时间、空间复杂度。如果内存空间不紧张,对执行效率要求很高,可以降低装载因子的阈值。如果内存紧张,对执行效率要求不高,可以增加装载因子的阈值。
如何避免低效地扩容?
为了解决一次性扩容耗时过多的情况,可以将扩容操作穿插在插入操作的过程中,分批完成。当装载因子到阈值之后,只申请新空间,但并不将老数据搬移到新散列表中。当有新数据要插入时,将新数据插入到新散列表中,并从老散列表中拿出一个数据放入到新散列表。
如何选择冲突解决方法?
1 开放寻址法
优点:散列表中的数组都存储在数组中,可以有效的利用CPU缓存加快查询速度。且序列化简单。
缺点:删除数据比较麻烦,需要特殊标记已经删除掉的数据。冲突代价更高。更浪费内存空间。
适合场景:数据量比较小,装载因子小。
2 链表法
优点:对内存地利用率高。对大装载因子地容忍度更高。
缺点:比较消耗内存。对CPU缓存不友好。
适合场景:大对象、大数据量
优化:将链表法中的链表改造为其他高效的动态数据结构,比如跳表、红黑树。