哈希
(二)哈希表的桶个数为什么是质数,合数有何不妥?
- 哈希表的桶个数使用质数,可以最大程度减少冲突概率,使哈希后的数据分布的更加均匀。
- 如果使用合数,可能会造成很多数据分布会集中在某些点上,从而影响哈希表效率。
(三) Hash表如何rehash,以及怎么处理其中保存的资源
- C++的hash表中有一个负载因子loadFactor,当loadFactor<=1时,hash表查找的期望复杂度为O(1)。
- 因此,每次往hash表中添加元素时,我们必须保证是在loadFactor <1的情况下,才能够添加。
- 因此,当Hash表中loadFactor==1时,Hash就需要进行rehash。
- rehash过程中,会模仿C++的vector扩容方式,Hash表中每次发现loadFactor ==1时,就开辟一个原来桶数组的两倍空间,称为新桶数组,然后把原来的桶数组中元素全部重新哈希到新的桶数组中。
(四) Hashmap
1、与map的区别
- map:
存放成对的数据key和value,key值是唯一的;查找信息麻烦,速度慢 - Hashmap:
基于哈希表。哈希表的优点是降低数据的存储和查找时间,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。只允许一条记录的键(key)为 null;无序的;查询效率很高。 - 构造函数。hash_map需要hash函数,等于函数;map只需要比较函数(小于函数).
- 存储结构。hash_map采用数组+链表的方式存储,map一般采用红黑树(RB Tree)实现。
- 权衡三个因素来决定使用map还是hashmap: 查找速度, 数据量, 内存使用。
2、基本原理
- 使用一个下标范围比较大的数组来存储元素。
- 可以设计一个函数(哈希函数,也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标,hash值)相对应,于是用这个数组单元来存储这个元素;
- 但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。 总的来说,“直接定址”(hash函数)与“解决冲突”(比较函数)是哈希表的两大特点
3、存值过程
- 先分配一大片内存,形成许多桶。利用哈希函数,将key映射到不同区域(桶)。
- 插入过程:得到key——通过哈希函数得到哈希值——得到桶号(可以是hash值对桶数求模)——存放key-val
- 取值过程:得到key——通过hash函数得到哈希值——得到桶号——比较桶内部的元素是否与key相等——取出相等的记录的value
4、Hash的桶
- 桶的个数越多,hash函数发生冲突的概率就越小,重新申请内存的概率就越小。n越大,效率越高,但是内存消耗也越大。
4、 实现原理
- 采用数组+链表的方式来实现对数据的储存
- 由于我们的数组的值是限制死的,我们在对key值进行散列取到下标以后,放入到数组中时,难免出现两个key值不同,但是却放入到下标相同的格子中,此时我们就可以使用链表来对其进行链式的存放。
- ⽤LinkedList代替数组结构可以吗?
- 可以。但是⽤数组效率最⾼!