一、什么是哈希表?
哈希表(英文名字为Hash table,我们也可能会在一些算法书籍上看到散列表,其实就是哈希表,但是会有翻译的不同,所以我们知道hash table是什么就可以了)。
哈希表是根据关键码的值而直接进行访问的数据结构。
书本上的解释看起来可能没有什么头绪,其实直白来讲其实数组就是一张哈希表。
当我们使用数组实现哈希表时,此时哈希表中的关键码就是数组的下标,也就是说我们可以直接通过下标访问对应的元素,如下图所示:
二、哈希表的功能
哈希表一般都是用来快速判断元素是否出现指定的容器中。
例如要查询一个学生是否在这所学校里。
直接遍历枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
因为我们可以初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
那么,现在关键的问题就是如何将学生一一对应的存放到哈希表里。换句话说,我们想要将学生与哈希表的关键码构成对应关系,也就是一种映射关系,那么,我们可能会想到数学中的函数,它就是自变量与因变量的一种对应法则。所以自然而然就出现了hash function ,也就是哈希函数。
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
如果hashCode得到的数值大于 哈希表的大小了,也就是大于tableSize了,怎么办呢?
此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做一个取模的操作,就要我们就保证了学生姓名一定可以映射到哈希表上了。
但仍然会存在着问题,我们刚刚说过哈希表就是一个数组。如果学生的数量大于哈希表的大小,此时就算哈希函数计算的再均匀,也避免不了会有几位学生的名字同时映射到哈希表同一个索引下标的位置,这种情况我们称作哈希碰撞。所以为了避免这种情况,我们一般要尽量保证哈希表的大小大于存放的数据大小。
三、常见的三种哈希结构
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构:
1. array(数组容器)
2. set(集合容器)
3. map(映射容器)
其中set和map有又可以分为有序不重复、有序重复multi、无序不重复unordered的三种结构
当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的;如果需要集合是有序的,那么就用set;如果要求不仅有序还要有重复数据的话,那么就用multiset。
当我们遇到需要快速判断元素是否出现容器中时,就要考虑使用哈希法了。
从实质上来看,使用哈希法进行查找也是一种牺牲空间换取时间的算法,因为我们要使用额外的数组,set或者是map等结构来存放数据,才能实现快速的查找。