文章目录
前言
在学习完map和set后,我们会惊讶于它的效率O(logn)。
但是unordered_map和unordered_set的效率是O(1),而它的底层原理就是哈希表
一、什么是哈希/散列?
构造一种存储结构,通过某种函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)
二、什么是哈希冲突?如何解决
不同关键字通过相同哈希哈数计算出相同的哈希地址,会导致哈希冲突
1.开放定制法(闭散列)
- 线性探测
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。 - 二次探测
找下一个空位置的方法为: = ( + )% m,或者: = ( - )% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小
2.拉链法(开散列)
开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
三、如果哈希表中冲突得很厉害怎么办?
1.控制负载因子
2.使用拉链法,如果一个冲突链到达一定长度,就不用链表,转换成红黑树
四、unordered_map和unordered_set跟map/set的区别?
一个底层是哈希表,时间复杂度O(1),遍历出来无序
一个是红黑树,时间复杂度O(logN) ,遍历出来有序
map和set是双向迭代器,unordered_map和unordered_set是单向迭代器
总结
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。
面试题:
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?
解决方案:
1.排序,二分查找
2.set/unordered_set(红黑树/哈希表)存起来再查找
以上方案的问题:数据量太大,放不到内存(16G)
那么解决方法是:位图,高效且节省空间 (500M)