一、概念
当向该结构中:
● 插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
● 搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表),时间复杂度为 O(1)或近似的为 O(1);
二、什么是 hash 冲突
如果两个不同的 key,通过哈希函数计算后得到了相同的 hash 值,此时这种情况称为 “ 哈希冲突 ” / " 哈希碰撞";
把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
. 哈希函数的一个特点:两个相同的 key ,计算得到的 hash 值一定相同,但是两个相同的 hash 值 对应的 key 不一定相同;
三、如何处理冲突
-
尽量避免(降低冲突概率):选择合适的 hash 函数
如果 key 为整数,hash 值计算方式只要是通过除留余数的方式,一般还会搭配一些其他的方法;
如果 key 为字符串,通常是通过 md5 sha1 来计算字符串的哈希值; -
正面应对(当冲突了后通过一定的方法解决冲突)
a).闭散列::也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把 key 存放到冲突位置中的“下一个” 空位置中去。
比散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。b).开散列:开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。
四、负载因子
当冲突比较严重时,查找 / 删除的时间复杂度会大大增加
例如:给定的数组只有 120 个元素;而实际上有 100 个元素需要保存,此时冲突的概率就比较高,当给定的数组有 1000 个元素,实际要保存 100 个元素,此时冲突就比较低;
从而引入 负载因子 α = 哈希表中保存的元素个数 / 数组长度
当冲突率达到一个无法忍受的程度时,
- 我们需要通过降低负载因子来变相的降低冲突率。已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小,即扩容,但是成本比较高(不太推荐);
- 针对开散列来说(链表),把链表替换成更高效的数据结构,比如 二叉搜索书 或者 哈希表