• 2022-07-05 数据结构与算法-散列表、哈希算法


    散列表

    散列表⽤的是数组⽀持按照下标随机访问数据的特性,所以散列表其 实就是数组的⼀种扩展,由数组演化⽽来。可以说,如果没有数组,就没有散列表。

    组成

    散列函数

    Hash(key)

    1. 直接定址法:Hash(key)=a*key+b (a、b为常数)
    2. 数字分析法
    3. 平方取中法
    4. 折叠法
    5. 除留余数法:Hash(key)=key mod p (p是一个整数(最好是小于等于表长的质数))
    6. 随机数法

    散列冲突

    • 开放定址法(Open addressing)
      • 线性探测法:一旦冲突,就找下一个地址,直到找到空地址存入,增量序列为1,2,3,4…
      • 二次探测法:一旦冲突,就找下一个地址,直到找到空地址存入,增量序列为12,-12,22,-22…,q2
      • 伪随机探测法:一旦冲突,就找下一个地址,直到找到空地址存入,增量序列为伪随机数

    数据量比较小、装载因子小的时候,适合采用开放定址法

    • 拉链法(chaining) 相同散列地址的记录链成一个单链表

    基于链表的散列冲突处理⽅法⽐较适合存储⼤对 象、⼤数据量的散列表,⽽且,⽐起开放寻址法,它更加灵活,⽀持 更多的优化策略,⽐如⽤红⿊树代替链表

    ⼯业级散列表举例因素分析

    1. 初始⼤⼩
    2. 装载因⼦和动态扩容
    3. 散列冲突解决⽅法
    4. 散列函数

    应用实例

    LRU缓存淘汰算法

    需要的操作:

    • 往缓存中添加⼀个数据;
    • 从缓存中删除⼀个数据;
    • 在缓存中查找⼀个数据。

    解决方案:

    • LinkedHashMap,在散列的基础上,增加添加元素有序

    Redis有序集合

    需要的操作:

    • 添加⼀个成员对象;
    • 按照键值来删除⼀个成员对象;
    • 按照键值来查找⼀个成员对象;
    • 按照分值区间查找数据,⽐如查找积分在[100, 356]之间的成员 对象;
    • 按照分值从⼩到⼤排序成员变量;

    解决方案:

    • 散列+跳表

    LinkedHashMap

    LinkedHashMap是通过双向链表和 散列表这两种数据结构组合实现的。LinkedHashMap中 的“Linked”实际上是指的是双向链表,并⾮指⽤链表法解决散列冲突

    哈希算法

    将任意⻓度的⼆进制值串映射为固定⻓度的⼆进制值串,这个映射的规则就是哈希算法,⽽通过原始数据映射之后得到的⼆进制值串就是哈希值

    需要满足的条件

    • 从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希 算法);
    • 对输⼊数据⾮常敏感,哪怕原始数据只修改了⼀个Bit,最后得到 的哈希值也⼤不相同;
    • 散列冲突的概率要很⼩,对于不同的原始数据,哈希值相同的概 率⾮常⼩;
    • 哈希算法的执⾏效率要尽量⾼效,针对较⻓的⽂本,也能快速地 计算出哈希值。

    应用

    安全加密

    1. MD5(MD5 Message-Digest Algorithm,MD5消息 摘要算法)
    2. SHA(Secure Hash Algorithm,安全散列算法)
    3. DES(Data Encryption Standard,数据加密标准)
    4. AES(Advanced Encryption Standard,⾼级加密标准)

    发生脱库解决方案:

    • 主要为了增加破解难度
    • 要维护⼀个常⽤密码的字典表,把字典中的每个密码⽤哈 希算法计算哈希值,然后拿哈希值跟脱库后的密⽂⽐对
    • 引⼊⼀个盐(salt),跟⽤户的密码组合在⼀ 起,增加密码的复杂度

    唯一标识

    获取数据固定片段(如前100、中100、后100字节)进行哈希算法,根据哈希值再进行判断可以节省大量计算。

    数据校验

    基于P2P协议文件下载,将一个文件分为多个文件块,可以对每个文件块进行哈希算法,保证数据安全。

    散列函数

    保证哈希冲突小,出现冲突时,使用开放定址法、拉链法解决冲突。

    负载均衡

    保证会话粘滞(Session sticky),需要在同⼀个客户端上,在⼀次会话中的所有请求都 路由到同⼀个服务器上。

    可以通过 哈希算法,对客户端IP地址或者会话ID计算哈希值,将取得的哈希值 与服务器列表的⼤⼩进⾏取模运算,最终得到的值就是应该被路由到 的服务器编号。

    数据分片

    两个例子

    1. 如何统计“搜索关键词”出现的次数?

    以先对数据进⾏分⽚,然后采⽤多台机器处 理的⽅法,来提⾼处理速度;

    为了提⾼处理的速 度,我们⽤n台机器并⾏处理。我们从搜索记录的⽇志⽂件中,依次读 出每个搜索关键词,并且通过哈希函数计算哈希值,然后再跟n取模, 最终得到的值,就是应该被分配到的机器编号。

    1. 如何快速判断图⽚是否在图库中?

    同样可以对数据进⾏分⽚,然后采⽤多机处理。我们准备n台机 器,让每台机器只维护某⼀部分图⽚对应的散列表。我们每次从图库 中读取⼀个图⽚,计算唯⼀标识,然后与机器个数n求余取模,得到的 值就对应要分配的机器编号,然后将这个图⽚的唯⼀标识和图⽚路径 发往对应的机器构建散列表;

    当要判断⼀个图⽚是否在图库中的时候,我们通过同样的哈希算 法,计算这个图⽚的唯⼀标识,然后与机器个数n求余取模。假设得到 的值是k,那就去编号k的机器构建的散列表中查找。

    分布式存储

    问题原因:

    • 但是,如果数据增多,原来的10个机器已经⽆法承受了,我们就需要 扩容了,⽐如扩到11个机器,这时候⿇烦就来了;

    • 原来的数据是通过与10来取模的。⽐如13这个数据,存储在编号为3 这台机器上。但是新加了⼀台机器中,我们对数据按照11取模,原来13这个数据就被分配到2号这台机器上了。

    解决方案:⼀致性哈希算法

    • 假设有k个机器,数据的哈希值的范围是[0, MAX]。我们将整个范 围划分成m个⼩区间(m远⼤于k),每个机器负责m/k个⼩区间。当 有新机器加⼊的时候,就将某⼏个⼩区间的数据,从原来的机器 中搬移到新的机器中。这样,既不⽤全部重新哈希、搬移数据,也保持了各个机器上数据数量的均衡;
    • ⼀致性哈希算法的基本思想就是这么简单。除此之外,它还会借助⼀ 个虚拟的环和虚拟结点,更加优美地实现出来;
  • 相关阅读:
    MySQL的特性
    微服务从代码到k8s部署应有尽有系列(三、鉴权)
    SpringBoot项目中后来添加的.gitignore文件使其生效,删除远端原有的target等目录
    Java架构师面试最全100篇(2022最新版)
    RealSenseSR300工程环境配置说明
    nginx重写与防盗链
    深入了解海豚调度DolphinScheduler
    github的博客搭建以及标签的自动化
    Java 7 生命周期结束
    Mysql生成数据字典
  • 原文地址:https://blog.csdn.net/qq_19152901/article/details/125630080