Java 中的双重哈希

2025年3月17日 | 阅读 8 分钟

在编程中,当我们处理数据结构时,有时需要存储具有相同哈希值的两个对象。存储具有相同哈希值的两个对象是不可能的。为了解决这个问题,数据结构提供了冲突解决技术。在本节中,我们将只关注双重散列、其优点、示例公式

数据结构提供了以下冲突解决技术

  1. 分离链接(也称为开放散列)
  2. 开放寻址(也称为封闭散列)
  • 线性探测
  • 二次探测
  • 双重散列

什么是双重散列?

它是一种用于避免冲突的开放寻址哈希表中的冲突解决技术。当两个键被散列到哈希表中的同一个索引时,就会发生冲突。发生冲突的原因是哈希表中的每个槽位都应该存储单个元素。

通常,散列技术包含一个哈希函数,该函数接受一个键并为该键生成哈希表索引。双重散列技术使用两个哈希函数,因此称为双重散列。如果第一个哈希函数产生冲突,第二个哈希函数会提供一个偏移量。换句话说,我们可以说当两个不同的对象具有相同的哈希值时,称为冲突

哈希函数

哈希函数是一个接受一组字符(键)并将其映射到特定长度的值(称为哈希值或散列)的函数。这个过程称为散列。它用于数据库中的索引和定位项。它提供了一种简单的方法来查找与较短哈希值关联的较长值。它广泛用于加密。它也被称为哈希算法消息摘要函数

双重哈希函数

第一个哈希函数确定查找键的初始位置,第二个哈希函数用于确定探测序列中跳跃的大小。以下函数是双重散列的示例

在上述函数中,i 的值会不断递增,直到找到一个空槽。

如果表的大小是素数,双重散列效果很好。

其中 PRIME 是小于 tableSize 的素数。

如果上述函数计算出的偏移量已被其他对象占用,则表示存在冲突。一个好的哈希函数必须具备以下属性

  • 快速计算。
  • 与原始哈希函数不同。
  • 永远不等于 0。

双重散列的优点

  • 该技术不会产生任何聚集。
  • 它是最好的探测形式,因为它比线性探测更快地找到哈希表中的下一个可用槽。
  • 它在整个哈希表中产生记录的均匀分布。

双重散列示例

假设我们有一个大小为11的哈希表。我们想在哈希表中插入键20, 34, 45, 70, 56。让我们使用以下双重哈希函数将键插入哈希表中

h1(k) = k mod 11         (第一个哈希函数)

h2(k) = 8 - (k mod 8)         (第二个哈希函数)

首先,我们将创建一个大小为 11 的哈希表。

让我们逐个插入键。

步骤:哈希函数索引描述
120h1(20) = 20 mod 119没有发生冲突。
234h1(34) = 34 mod 111没有发生冲突。
345h1(45) = 45 mod 111发生冲突,因为索引 1 已被 34 占用。现在我们将使用第二个哈希函数计算键 45 的索引。
h2(45) = 8 - (45 mod 8) = 3
h(45, 1) = (1 + 1 * 3) mod 11
4这里,我们取 i 的值为 1,因为发生了第一次冲突。请注意,此处 h2 (k) 和 11 是互质的。h2 (k) 的值必须小于表的大小。
470h1(70) = 70 mod 114发生冲突,因为索引 4 已被 45 占用。现在我们将使用第二个哈希函数计算键 70 的索引。
h2(70) = 8 - (70 mod 8) = 2
h(70, 1) = (4 + 1 * 2) mod 11
6这里,我们取 i 的值为 1,因为发生了第一次冲突。请注意,此处 h2 (k) 和 11 是互质的。h2 (k) 的值必须小于表的大小。
556h1(56) = 56 mod 111发生冲突,因为索引 1 已被 34 占用。现在我们将使用第二个哈希函数计算键 56 的索引。
h2(56) = 8 - (56 mod 8) = 8
h(56, 1) = (1 + 1 * 8) mod 11
9再次发生冲突。索引 9 已被 20 占用。
h(56, 2) = (1 + 2 * 8) mod 116此处,i 的值增加 1,因为发生了第二次冲突。我们注意到又发生了冲突,因为索引 6 已被 70 占用。
h(56, 3) = (1 + 3 * 8) mod 113此处,i 的值增加 1。第三个索引是空的。因此,我们将 56 存储在索引 3。

*粗体显示的索引表示冲突。

插入所有键后,哈希表如下所示。

Double Hashing in Java

现在我们已经清楚地理解了双重散列。因此,我们可以轻松地区分线性探测和二次探测。

在线性探测中,如果在任何索引处发生冲突,我们会查找紧邻的下一个索引。如果下一个索引也被占用,我们会查找紧邻的下一个索引。这个过程会重复,直到找到一个空索引。

在二次探测中,如果在任何索引处发生冲突,我们会查找紧邻的下一个索引。如果下一个索引也被占用,那么我们将跳转到 i2 索引。

假设索引 2 已被占用,那么我们将查找 22,即 4。这意味着从当前索引(即 2)向前查找 4 个索引。

同样,如果索引 3 也被占用,那么我们将查找 32,即 9。这意味着从当前索引(即 3)向前查找 9 个索引。

双重散列插入元素的算法

  1. 设置 index = H(K); offset = H2(K)
  2. 如果表位置 index 已经包含该键,则无需插入。完成!
  3. 否则,如果表位置 index 为空,则在此处插入键。完成!
  4. 否则,发生冲突。设置 index = (index + offset) mod M。
  5. 如果 index == H(K),则表已满!(抛出异常或扩大表。)否则转到步骤 2。

让我们在 Java 程序中实现上述算法。

双重散列 Java 程序

在以下 Java 程序中,我们在哈希表中实现了双重散列。

DoubleHashingExample.java

输出

Double Hashing in Java
下一主题Java 中的魔方