12.散列表(上)

1.概念

散列表用的是数组支持按照下标随机访问的特性,所以散列表其实就是数组的一种扩展,由数组演化而来

key: key.

散列函数(hash函数):把key转化为hash值的函数

散列值(hash值):数组的下标.

hash = hash_function(key)

​ 对hash函数的基本要求:

  • 散列函数计算得到的散列值是一个非负数整数:对于数组的下标,从0开始

  • 如果 key1 == key2 那么 hash(key1) == hash(key2)

  • 如果 key1 != key2 那么 hash(key1) != hash(key2)

    对于第三点,其实无法完全避免,满足这一点的hash函数几乎不存在。当 key1 != key2时, hash(key1) 有可能等于 hash(key2), 尤其当数组的空间有限,这中可能性就更大。这种情况较 hash冲突

再好的hash函数也无法避免hash冲突的问题。解决hash冲突有两个思路:

  • 开放寻址法(Open addressing)
  • 链表法(chaining)

2.实现

2.1 开放寻址法

​ 核心思路:如果出现了散列冲突,就重新探测一个空闲的位置,将其插入。如何重新探寻新的位置?一个比较简单的探测方法,线性探测:如果某个数据经过散列之后,存储位置已经被占用了,就从当前位置开始,依次往后查找,直达找到空闲位置为止。

在这里插入图片描述

​ 上图中,橙色表示 已经有元素,黄色表示该位置空闲。hash之后,得到位置为7。此时7已经有元素,因此顺序往下,8,9都有元素,从头开始,0,1,直至找到x.

  • 插入

    按之前线性探方法直至找到空闲位置。时间复杂度为:
    最好:下一个元素有空位 O(1)
    最坏:上一个相邻位置才有空位,需要遍历n-1-> O(n)
    平均:(1+2+3+。。。+n-1)/(n-1) = n/2 -> O(n)
    
  • 删除

    如果直接把找到的元素置空,则有问题,因为无法知道是否真正删除了元素。可以把这个元素标记为为"deleted"状态。当遍历至这个元素时,如果key一样,状态已经标记为deleted,则说明已经删除。如果无找不到,则须一次遍历查找,然后删除
    
  • 查询

    先通过hash查找,O(1). 如果找到hash(key),比较key,如果key一只,则结束。如果key不一致,则通过线性查找,直至找到,平均为O(n)。如果该位置空闲,则说明不在数组中。
    

    其他开放寻址方法:

    • 二次探测

      步长不为1,而是为原来2次方,即:
      hash(key) + 0, hash(key)+1, hash(key)+2, ... 
          变为
      hash(key) + 2^0, hash(key)+2^1, hash(key)+2^2, ... 
      
    • 双重散列

      使用一组hash函数,而非一个hash函数:{hash1, hash2, hash3, hash4, ...},hash1(key)已经有值,则用hash2(key), 依次类推,直至找到空闲位置.
      

      不管采用哪种探测方法,当散列中空闲位置不多时,散列冲突的概率就会大大提高。为了尽可能保证散列列表的操作效率,一般情况下,我们会尽力可能保证散列中有一定比例的空闲槽位。我们用装载因子(load factor) 来表示空位的多少

      	装载因子 = 填入表中的元素个数 / 散列表的长度
      

      装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

2.2 链表法

​ 链表法是一种更加常用的散列冲突解决办法,相对开放寻址法,它要简单很多。如下图。在散列中,每个”桶(bucket)“或则”槽(slot)“ 会对应一条链表,hash(key)相同的元素都会放入相同槽位对应的链条中:

在这里插入图片描述

时间复杂度:

对于插入和删除,

  • 最好: 数组每个位置都直接插入,无需链表,为O(1)
  • 最坏:每次hash(key)的值都一样,此时,变成了一条链表,复杂度为O(n)
  • 平均:均匀分布,假设有m个桶,则每条链的长度为 n/m, 所有复杂度为 O(k) = O(n/m)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值