0
点赞
收藏
分享

微信扫一扫

小白学数据结构和算法: 哈希数据结构实现原理

小白学数据结构和算法: 哈希数据结构实现原理_数据结构

使用哈希函数计算哈希值的复杂度

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

哈希问题

如果我们考虑上面的例子,我们使用的哈希函数是字母的总和,但是如果我们仔细检查哈希函数,那么问题可以很容易地可视化,对于不同的字符串,哈希函数开始生成相同的哈希值。 

例如:{“ab”,“ba”}具有相同的哈希值,字符串{“cd”,“be”}也生成相同的哈希值等。这称为冲突,它会在搜索中产生问题、值的插入、删除和更新。 

什么是碰撞?

散列过程为大密钥生成较小的数字,因此两个密钥有可能产生相同的值。新插入的键映射到已占用的键的情况,必须使用某种碰撞处理技术来处理。

小白学数据结构和算法: 哈希数据结构实现原理_hash_02

什么是哈希中的碰撞

如何处理碰撞?

处理碰撞主要有两种方法: 

  1. 单独链接:
  2. 开放寻址: 

小白学数据结构和算法: 哈希数据结构实现原理_python_03

碰撞解决技术

1)单独链接

其思想是使散列表的每个单元指向具有相同散列函数值的记录的链表。链接很简单,但需要表外的额外内存。

示例:我们给定了一个哈希函数,我们必须使用单独的链接方法在哈希表中插入一些元素以实现冲突解决技术。

Hash function = key % 5, 
Elements = 12, 15, 22, 25 and 37.

让我们逐步看看如何解决上述问题:

  • 步骤1:首先绘制一个空的哈希表,根据提供的哈希函数,哈希值的可能范围为0到4。
     

小白学数据结构和算法: 哈希数据结构实现原理_数据结构_04

哈希表

  • 步骤2:现在将哈希表中的所有键一一插入。第一个插入的key是12,映射到桶号2,通过哈希函数12%5=2计算得到。

小白学数据结构和算法: 哈希数据结构实现原理_算法_05

插入12

  • 步骤3:现在下一个键是22。它将映射到桶号2,因为22%5=2。但存储桶 2 已经被键 12 占用。

小白学数据结构和算法: 哈希数据结构实现原理_算法_06

插入22

  • 步骤 4:下一个键是 15。它将映射到槽号 0,因为 15%5=0。

小白学数据结构和算法: 哈希数据结构实现原理_python_07

插入15

  • 步骤5:现在下一个键是25。它的桶号将是25%5=0。但是桶 0 已经被键 25 占用。因此单独的链接方法将通过创建到桶 0 的链表来再次处理冲突。

小白学数据结构和算法: 哈希数据结构实现原理_python_08

插入25

因此,以这种方式,使用单独链接方法作为冲突解决技术。

2)开放寻址

在开放寻址中,所有元素都存储在哈希表本身中。每个表条目包含一条记录或 NIL。查找元素时,我们会逐个检查表槽,直到找到所需元素或者明确该元素不在表中。

2.a) 线性探测

在线性探测中,从哈希的原始位置开始顺序搜索哈希表。如果我们得到的位置已被占用,那么我们检查下一个位置。 

算法步骤:

  1. 计算哈希键。即键=数据%大小
  2. 检查hashTable[key]是否为空
  • 直接通过hashTable[key] = data存储值
  1. 如果哈希索引已经有一些值那么
  1. 使用key = (key+1) % size 检查下一个索引
  1. 检查下一个索引是否可用 hashTable[key] 然后存储该值。否则尝试下一个索引。
  2. 重复上述过程,直到找到空间。

示例:让我们考虑一个简单的哈希函数“key mod 5”,要插入的键序列是 50、70、76、85、93。 

  • 步骤1:首先绘制一个空的哈希表,根据提供的哈希函数,哈希值的可能范围为0到4。

小白学数据结构和算法: 哈希数据结构实现原理_python_09

哈希表

  • 步骤2:现在将哈希表中的所有键一一插入。第一个键是 50。它将映射到槽号 0,因为 50%5=0。因此将其插入插槽号 0 中。

小白学数据结构和算法: 哈希数据结构实现原理_hash_10

将 50 插入哈希表

  • 步骤3:下一个键是70。它将映射到槽号0,因为70%5=0,但50已经在槽号0处,所以,搜索下一个空槽并将其插入。

小白学数据结构和算法: 哈希数据结构实现原理_算法_11

将 70 插入哈希表

  • 步骤 4:下一个键是 76。它将映射到插槽编号 1,因为 76%5=1,但 70 已经位于插槽编号 1,因此,搜索下一个空插槽并将其插入。

小白学数据结构和算法: 哈希数据结构实现原理_程序员_12

将 76 插入哈希表

  • 步骤5:下一个键是93,它将映射到槽号3,因为93%5=3,所以将其插入槽号3。

小白学数据结构和算法: 哈希数据结构实现原理_程序员_13

将 93 插入哈希表

2.b) 二次探测

二次探测是计算机编程中的一种开放寻址方案,用于解决哈希表中的哈希冲突。二次探测的操作方式是获取原始哈希索引并添加任意二次多项式的连续值,直到找到空槽。

使用二次探测的示例序列是:

H + 1 2H + 2 2H + 3 2H + 4 2 ………………。H + k 2

该方法也称为中方法,因为在该方法中,我们在第 i 次迭代中查找第 i 个探针(槽),并且 i = 0, 1, ... 的值。。。n – 1。我们总是从原始哈希位置开始。如果只有该位置被占用,那么我们检查其他插槽。

令 hash(x) 为使用哈希函数计算的槽索引,n 为哈希表的大小。

如果槽 hash(x) % n 已满,那么我们尝试 (hash(x) + 1 2 ) % n。
如果 (hash(x) + 1 2 ) % n 也满了,那么我们尝试 (hash(x) + 2 2 ) % n。
如果 (hash(x) + 2 2 ) % n 也满了,那么我们尝试 (hash(x) + 3 2 ) % n。将对i
的所有值重复此过程,直到找到空槽为止

示例:让我们考虑表大小 = 7,哈希函数为 Hash(x) = x % 7 ,冲突解决策略为 f(i) = i 2。插入 = 22、30 和 50

  • 步骤1:创建一个大小为7的表。

小白学数据结构和算法: 哈希数据结构实现原理_hash_14

哈希表

  • 步骤 2 – 插入 22 和 30
  • Hash(22) = 22 % 7 = 1,由于索引 1 处的单元格为空,因此我们可以轻松地在槽 1 处插入 22。
  • Hash(30) = 30 % 7 = 2,由于索引 2 处的单元格为空,因此我们可以轻松地在槽 2 处插入 30。 

小白学数据结构和算法: 哈希数据结构实现原理_数据结构_15

将键 22 和 30 插入哈希表中

  • 第 3 步:插入 50
  • 哈希值(50) = 50 % 7 = 1 
  • 在我们的哈希表中,槽 1 已经被占用。因此,我们将搜索槽 1+1 2,即 1+1 = 2, 
  • 再次发现插槽 2 被占用,因此我们将搜索单元格 1+2 2,即 1+4 = 5, 
  • 现在,单元格 5 未被占用,因此我们将 50 放入槽 5 中。

小白学数据结构和算法: 哈希数据结构实现原理_hash_16

在哈希表中插入键 50

2.c) 双重哈希

双散列是开放寻址散列表中的一种冲突解决技术。双散列利用两个散列函数, 

  • 第一个哈希函数是h1(k),它获取密钥并给出哈希表上的位置。但如果新位置未被占用或空着,那么我们可以轻松放置钥匙。 
  • 但如果位置被占用(冲突),我们将使用辅助哈希函数h2(k)与第一个哈希函数h1(k)结合来查找哈希表上的新位置。 

这种哈希函数的组合具有以下形式 

h(k, i) = (h1(k) + i * h2(k)) % n

在哪里 

  • i 是一个非负整数,表示碰撞次数,
  • k = 正在散列的元素/键
  • n = 哈希表大小。

双哈希算法的复杂度: 

时间复杂度:O(n)

示例:将键 27, 43, 692, 72 插入大小为 7 的哈希表中。其中第一个哈希函数是h1(k) = k mod 7,第二个哈希函数是h2(k) = 1 + (k模 5)

  • 第 1 步:插入 27
  • 27 % 7 = 6,位置 6 为空,因此将 27 插入到 6 插槽中。

小白学数据结构和算法: 哈希数据结构实现原理_程序员_17

在哈希表中插入键 27

  • 第 2 步:插入 43
  • 43 % 7 = 1,位置 1 为空,因此将 43 插入 1 个槽中。

小白学数据结构和算法: 哈希数据结构实现原理_算法_18

在哈希表中插入键 43

  • 步骤 3:插入 692
  • 692 % 7 = 6,但位置 6 已被占用,这是一次冲突
  • 所以我们需要使用双重哈希来解决这种冲突。

h new = [h1(692) + i * (h2(692)] % 7 
= [6 + 1 * (1 + 692 % 5)] % 7 
= 9 % 7 
= 2

现在,由于 2 是一个空槽,
所以我们可以将 692 插入第二个插槽。

小白学数据结构和算法: 哈希数据结构实现原理_数据结构_19

在哈希表中插入键 692

  • 第 4 步:插入 72
  • 72 % 7 = 2,但位置 2 已被占用,这是一次冲突。 
  • 所以我们需要使用双重哈希来解决这种冲突。

h new = [h1(72) + i * (h2(72)] % 7 
= [2 + 1 * (1 + 72 % 5)] % 7 
= 5 % 7 
= 5,

现在,因为 5 是一个空槽,
所以我们可以将 72 插入到第 5 个槽中。

小白学数据结构和算法: 哈希数据结构实现原理_数据结构_20

在哈希表中插入键 72

哈希中的负载因子是什么意思?

哈希表的负载因子可以定义为哈希表包含的项数除以哈希表的大小。负载因子是当我们想要重新哈希以前的哈希函数或想要向现有哈希表添加更多元素时使用的决定性参数。

它帮助我们确定哈希函数的效率,即它告诉我们正在使用的哈希函数是否在哈希表中均匀分布键。

负载因子=哈希表中的元素总数/哈希表的大小

什么是重新哈希?

顾名思义,重新哈希意味着再次哈希。基本上,当负载因子增加到超过其预定义值(负载因子的默认值为 0.75)时,复杂性就会增加。因此,为了克服这个问题,数组的大小增加(加倍),所有值再次散列并存储在新的双倍大小的数组中,以保持低负载因子和低复杂性。

哈希数据结构的应用

  • 哈希在数据库中用于索引。
  • 哈希用于基于磁盘的数据结构。
  • 在Python等一些编程语言中,JavaScript哈希用于实现对象。 

哈希数据结构的实时应用

  • 哈希用于缓存映射以快速访问数据。
  • 哈希可用于密码验证。
  • 哈希在密码学中用作消息摘要。
  • 用于字符串中模式匹配的 Rabin-Karp 算法。 
  • 计算字符串中不同子串的数量。 

哈希数据结构的优点

  • 哈希提供比其他数据结构更好的同步。
  • 哈希表比搜索树或其他数据结构更有效
  • 哈希为搜索、插入和删除操作提供平均恒定的时间。

哈希数据结构的缺点

  • 当冲突较多时,哈希的效率很低。
  • 对于大量可能的键,实际上无法避免哈希冲突。
  • 哈希不允许空值。 

算法:计算给定总和的元素有哪些?

给定一个包含N 个整数的数组和一个整数K,任务是找到数组中总和等于K的整数对的数量。

例子:  

输入: 
输出: 
解释:和为 6 的对是 (1, 5) 和 (7, -1)。

输入: arr[] = {1, 5, 7, -1, 5}, K = 6
输出: 
解释:和为 6 的对是 (1, 5), (7, -1) & (1, 5) 。         


输入: 
输出: 
解释:和为 2 的对为 (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)。

输入: 
 
( 10 , 1), (10, 1), (10, 1), (12, -1), (10, 1), (10, 1), (10, 1), (7, 4), (6, 5)。

Python 解法:

# Python 3实现的简单方法 # 查找给定和的对数。# 返回 arr [0.n-1] # 中的对数,其中 sum 等于‘ sum’
def getPairsCount(arr, n, K):

	count = 0 # 

	# 考虑所有可能的配对
	# 并检查它们的和
	for i in range(0, n):
		for j in range(i + 1, n):
			if arr[i] + arr[j] == K:
				count += 1

	return count


# 驱动器功能
arr = [1, 5, 7, -1]
n = len(arr)
K = 6
print("Count of pairs is",	getPairsCount(arr, n, K))

PHP 解法:

<?php
// PHP实现简单的方法
// 用于找到给定和的对数。

// 返回arr[0..n-1]中和为'sum'的对数。
function getPairsCount($arr, $n, $sum)
{
// 初始化结果
	$count = 0; 
// 考虑所有可能的配对
// 并检查它们的和
	for ($i = 0; $i < $n; $i++)
		for ($j = $i + 1; $j < $n; $j++)
			if ($arr[$i] + $arr[$j] == $sum)
				$count++;

	return $count;
}


$arr = array(1, 5, 7, -1, 5) ;
$n = sizeof($arr);
$sum = 6;
echo "Count of pairs is ", getPairsCount($arr, $n, $sum);

?>

举报

相关推荐

0 条评论