数据结构——21.哈希

哈希(Hashing,也译为“散列”)是一种极其高效的数据结构,核心思想是通过一个函数直接计算出数据的位置,从而实现快速的查找、插入和删除。理想情况下,其时间复杂度可以达到 O(1)O(1)O(1)

第一部分:核心思想与基本概念

  1. 为什么需要哈希?

    • 我们之前学过的查找方法,如顺序查找、二分查找、二叉搜索树等,都需要通过一系列的“比较”操作来定位元素。
    • 哈希的革命性思想是:不通过比较,而是直接计算。给定一个关键字(Key),通过一个“哈希函数(Hash Function)”,直接算出它应该存储的地址。
  2. 关键术语

    • 哈希表(Hash Table):也叫散列表,是根据键(Key)而直接访问在内存存储位置的数据结构。你可以把它想象成一个数组,用于存放数据。
    • 槽(Slot):哈希表中的每个位置被称为一个槽。
    • 哈希函数(Hash Function / 散列函数):一个函数,输入是关键字 K,输出是该关键字在哈希表中的存储地址。通常记为 h(K)
    • 冲突(Collision):当两个不同的关键字 K1K2K1 ≠ K2),经过哈希函数计算后得到了相同的地址(h(K1) = h(K2))时,就发生了冲突。处理冲突是哈希技术的关键核心。

第二部分:设计优秀的哈希函数

一个好的哈希函数应该能将关键字尽可能均匀地散布到哈希表的各个位置上,以最大限度地减少冲突。

  • 设计原则

    1. 均匀性:对任意关键字,其哈셔值在所有地址上应等概率出现。
    2. 相关性:哈希值应与关键字的每一部分都有关,避免因关键字局部相似而导致冲突。
  • 常用构造方法(笔试重点)

    1. 除法散列法(Division Method)

      • 公式h(K)=K(modM)h(K) = K \pmod Mh(K)=K(modM)
      • 说明:这是最简单也最常用的方法。M 是哈希表的长度。
      • M 的选择技巧M 通常选择一个略大于或等于元素总数的质数。这可以有效减少因关键字本身规律性(如都是偶数)而导致的冲突。
    2. 乘法散列法(Multiplication Method)

      • 公式h(K)=⌊M×(Kθ(mod1))⌋h(K) = \lfloor M \times (K\theta \pmod 1) \rfloorh(K)=M×(Kθ(mod1))
      • 说明:其中 M 是表长,θ 是一个介于 0 和 1 之间的常数。Kθ mod 1 表示取 的小数部分。
      • θ 的选择技巧:实验表明,θ 取黄金分割比附近的值(约 0.618)时,效果较好。
    3. 平方取中法(Mid-Square Method)

      • 方法:取关键字的平方,然后取中间的若干位作为哈希地址。
      • 优点:关键字的每一位都对结果有影响,使得哈希值分布相对均匀。
      • 适用场景:适用于关键字的位数不是特别大的情况。
    4. 压缩法/折叠法(Folding Method)

      • 方法:将关键字的二进制串分割成等长的几部分,然后对这几部分进行运算(如异或、相加)得到哈希地址。
      • 注意:使用异或(XOR)运算时,像 STEAL, STALE, LEAST 这样字母相同但顺序不同的单词,会得到相同的哈希值,因为异或满足交换律。
    5. 抽取法(Extraction Method)

      • 方法:从关键字的二进制串中抽取其中分散的几位来构成地址。
      • 缺点:由于只用了部分信息,容易造成“群集”现象(即冲突集中在某些区域)。

第三部分:处理哈希冲突(核心考点)

既然冲突很难完全避免,我们就必须有高效的解决方案。主要有两大类方法:

1. 拉链法(Chaining / Separate Chaining)

  • 思想:将所有哈希地址相同的元素,存放在一个链表中。哈希表本身(通常是一个数组)存放的是这些链表的头指针。

  • 优点

    • 实现简单,逻辑清晰。
    • 对于冲突的处理非常自然,不会出现“聚集”现象。
    • 哈希表的装载因子 λ(元素个数/表长)可以大于 1。
    • 删除操作简单,直接操作链表即可。
  • 缺点

    • 需要额外的指针空间,存储开销较大。
  • 笔试模拟

    • 给定关键字序列 EN, TO, TRE, FIRE, FEM, SEKS, SYV
    • 哈希函数值为 2, 0, 3, 0, 4, 8, 1
    • 使用拉链法,最终哈希表结构如下:
      • HEAD[0] -> TO -> FIRE
      • HEAD[1] -> SYV
      • HEAD[2] -> EN
      • HEAD[3] -> TRE
      • HEAD[4] -> FEM
      • HEAD[5] -> (空)
      • HEAD[8] -> SEKS

2. 开放地址法(Open Addressing)

  • 思想:发生冲突时,不使用额外的数据结构(如链表),而是按照一个预定的探查序列,在哈希表中寻找下一个可用的空槽位来存放元素。

  • 探查序列生成方法

    a. 线性探查法(Linear Probing)
    * 探查规则:从冲突位置 h(K) 开始,依次检查 h(K)+1, h(K)+2, … 直到找到空位(需要对表长 M 取模)。
    * 优点:实现简单。
    * 缺点一次聚集(Primary Clustering)。冲突的元素容易在表中连成一片,导致查找效率急剧下降。

    b. 二次探查法(Quadratic Probing)
    * 探查规则:从 h(K) 开始,依次检查 h(K)+1², h(K)-1², h(K)+2², h(K)-2², … (同样需要取模)。
    * 优点:能有效缓解线性探查的“一次聚集”问题。
    * 实现注意:表长 M 最好是形如 4k+3 的素数,这样才能保证探查到所有位置。

    c. 双重散列/再散列(Double Hashing)
    * 探查规则:使用两个哈希函数 h1(K)h2(K)。第一个函数 h1 计算初始地址,第二个函数 h2 计算每次探查的步长。探查序列为 (h1(K) + i * h2(K)) % M
    * 优点:是效果最好的开放地址法之一,能极大地减少聚集现象。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值