散列表详解

散列表详解

  • 哈希函数和哈希值
  • 哈希碰撞
  • 拉链法
  • 线性探测法

哈希函数和哈希值

如果我们存储一些数据(以键值对的形式存储, 键为数字), 怎样能够让我们的查询速度达到最快呢? 我们如果用顺序查找, 那时间复杂度就是 O ( n ) O(n) O(n), 如果用二分查找或者二叉排序树之类的, 时间复杂度就是 O ( l o g n ) O(logn) O(logn), 那么我们在激进一点, 能不能是的查找的复杂度为 O ( 1 ) O(1) O(1)呢? 答案是能达到.
首先, 我们可以用数组来存储, 假设我们需要存储的数据的键最大只有100, 那我们就见一个数组values[100], 对于每一对数据{key:value}我们都用key[key]=value的形式来存储, 这样我们要查找key对应的值时, 是不是时间复杂度就只有 O ( 1 ) O(1) O(1)了?
但是, 这样存储有一个很大的问题, 那就是对于存储数据的键的范围很大的话, 我们就需要开很大的数组, 比如说如果我们的键的取值范围是 1 1 1 10000000000 10000000000 10000000000呢?显然不能够开这么大的数组, 而且就算能开这么大的数组, 我们也不会使这个数组的每一位都存上值, 也就是说这户造成很大的浪费. 那么, 有什么办法解决这个问题呢?
通常, 我们会用一种散列函数(哈希函数)来解决这种问题, 它能将范围很大的键处理成我们需要的大小范围内, 其中最简单的就是取模. 假设我们需要的取值范围是 100 100 100, 但是我们键的取值范围有 1000000 1000000 1000000, 我们可以用index = = =key%100, 这样我们就将处理后的键的取值限制在了 100 100 100以内了, 而这个值就是哈希值
试试上, 我们实际中使用的哈希函数比这个复杂, 而且它的输入可以是任意的基本类型, 不一定是数字类型

哈希碰撞

我们用哈希值来存储数据解决了空间浪费的问题, 但是用出现了新的问题: 假设我们用index = = =key%100来处理数据, 那么 1 1 1% 100 = 1 100=1 100=1, 101 101 101% 100 = 1 100=1 100=1, 两个不同的键产生了同样的哈希值, 那么怎样同时存储这两个数据呢? 这种情况就叫做哈希碰撞, 那么, 怎么解决哈斯碰撞呢?

拉链法

我们可以用链表存储
散列表详解_第1张图片
对于键哈希值相同的结点, 在数组对应的地方延伸出一条链表, 这样遇到同样哈希值的结点也能存储, 只是稍微牺牲了一点性能. 实际上, 我们能更进一步, 当链表足够大的时候, 我们可以将链表转化为二叉查找树或红黑树, 实际上容器类HashMap就是这么做的

线性探测法

我们可以用线性探测表来进行处理. 在我们用公式计算出哈希值时, 倘若数组中该哈希值所对应的地方已经存储了数据, 那么我们可以存储在其哈希值 + 1 +1 +1的位置上, 倘若该位置也非空, 就继续 + 1 +1 +1, 以此类推.
散列表详解_第2张图片
这种方法充分利用了散列表中的空位置, 但是存在一个问题: 用这种方法存储查询时的性能和和键簇长度有关, 键簇指的是表中连续非空的部分, 我们很轻易地能够想到, 当键簇很长是, 我们要存储一个元素或者查找一个一个元素可能需要移位很多次, 所以, 簇族长度越长, 散列表的平均性能越差

你可能感兴趣的:(散列表,数据结构,链表)