哈希表的概念,哈希表是什么
哈希表,又叫散列表,是根据关键字进行访问的数据结构
哈希表建立了一种关键字和存储地址之间的映射关系,使每个关键字与结构中的唯一存储
位置相对应。在理想情况下,散列表中进行查找的时间复杂度是O(1),与元素数量无关
接下来我们用一个案例来说明:统计一下字符串“abcabcc"中,每一个字符出现的次数,字符串只包含小写字母
代码
#include <iostream> using namespace std; int a[26]; int main() { string s; cin >> s; for(auto e : s) { a[e-'a']++; } return 0; }
这时候,我们的hash(key)也就是key-'a'
那么哈希冲突是啥呢?
哈希函数可能把两个或者两个以上的关键字映射到同一个存储位置上,这时候就是我们的哈希冲突,也叫散列冲突,起冲突的不同关键字称之为同义词
第一种方法就是 创建一个1e9+10大小的数组,用hash(key)=key来存储哈希值
但是这种方法是行不通的,我们创建这么大的数组空间复杂度太高了
所以我们要选第二种方法,我们可以对每个数模上7来代表存储位置,这样的话我们只需要创建一个大小为7的数组就足够了 我们的哈希函数也就是hash(key) = key%7
1%7 = 1, 3%7 = 3 17%7 = 3 100000000%7 = 6 49%7 = 0 49%7 = 0
可以看到有两个关键字的存储位置都是3,这两个关键字就是同义字
哈希冲突是不可避免的,我们要做的不是消除哈希冲突,我们要做的是处理哈希冲突和创造好的哈希函数,
常见的哈希函数,以及怎么处理哈希冲突
哈希函数1:直接定址法
直接取关键字的某个线性函数值作为散列地址
比如hash(key) = key,hash(key) = a*key+b
我们之前的案例
统计一下字符串“abcabcc"中,每一个字符出现的次数,字符串只包含小写字母
就是一种直接定址法
hash(key)=key-'a' 也就是1*key-'a' 是线性的
我们的直接定址法适合元素分布比较连续而不是很跳跃的,不然会很浪费我们的空间
哈希函数2:除留余数法
我们哈希冲突那里的案例,用的就是除留余数法,除留余数,顾名思义就是用key%M作为存储地址,我们的M尽量取成一个素数(减少哈希冲突),最好接近哈希表的大小
处理哈希冲突法1:线性探测法
这种方法就是,我们从发生哈希冲突的位置上依次向后探测,找到一个空位置把值放进去
如果走到表尾了,到下一个就回绕到表头
当我们用除留余数法适,我们的除数一般是选择原来数组长度的两倍附近的一个素数,因为如果我们只找一倍数组附近的素数的话,就显得很紧凑,一不小心的话我们的时间复杂度就会变成O(N),我们弄的大一点的话,我们查一个数的时候需要跳跃的格子相应的就会少
本题数组长度是8,2*8=16,16附近找一个素数,我们就找11吧为了更好看
hash(key)=key%11