什么是哈希?
哈希是一种将任意长度的数据转换为固定长度的数据的技术。这个固定长度的数据通常被称为哈希值或哈希码。哈希函数是实现这一转换的关键,它接受任意长度的输入,并产生一个固定长度的输出。
为什么使用哈希?
哈希的主要用途之一是快速查找数据。通过哈希函数,我们可以将数据映射到一个较小的范围,从而减少查找时间。此外,哈希还可以用于数据完整性检查、密码存储等场景。
哈希表抽象数据类型(ADT)
哈希表是一种基于哈希函数的数据结构,它允许我们以接近O(1)的时间复杂度进行插入、删除和查找操作。哈希表的核心思想是使用哈希函数将键映射到表中的一个位置,然后将键值对存储在该位置。
理解哈希
哈希函数的设计对于哈希表的性能至关重要。一个好的哈希函数应该能够均匀地分布键值对,以减少冲突的发生。冲突是指多个键通过哈希函数映射到同一个位置的情况。
哈希组件
哈希表由以下几个关键组件组成:
-
哈希函数:将键映射到表中的位置。
-
哈希表:存储键值对的数组。
-
负载因子:表中已使用位置与总位置数的比值。
-
冲突解决技术:处理多个键映射到同一个位置的情况。
哈希表
哈希表是实现哈希功能的数据结构。它通常是一个数组,数组的每个位置可以存储一个或多个键值对。
哈希函数
哈希函数是哈希表的核心,它将键转换为哈希表中的索引。一个好的哈希函数应该满足以下条件:
-
均匀分布:键值对在哈希表中均匀分布。
-
快速计算:哈希函数的计算应该快速且高效。
-
确定性:相同的键总是映射到相同的索引。
常见函数分类及示例
哈希函数是将任意长度的输入数据转换为固定长度输出的函数。一个好的哈希函数应该具有均匀分布、快速计算和确定性的特点。以下是一些常见的哈希函数分类及其示例:
1. 除法哈希函数(Division Hash Function)
除法哈希函数是最简单的哈希函数之一,它通过取模运算将输入数据映射到哈希表的索引。
公式
其中,k
是输入键,m
是哈希表的大小。
示例代码
def division_hash_function(key, table_size):
return key % table_size
# 示例输入输出
table_size = 10
print(division_hash_function(25, table_size)) # 输出: 5
print(division_hash_function(35, table_size)) # 输出: 5
2. 乘法哈希函数(Multiplication Hash Function)
乘法哈希函数通过乘法和取模运算将输入数据映射到哈希表的索引。这种方法通常使用一个常数 A
,通常选择为黄金分割比。
公式
其中,k
是输入键,m
是哈希表的大小,A
是一个常数(通常选择为黄金分割比 0.618033988749895)。
示例代码
def multiplication_hash_function(key, table_size):
A = 0.618033988749895
return int(table_size * ((key * A) % 1))
# 示例输入输出
table_size = 10
print(multiplication_hash_function(25, table_size)) # 输出: 4
print(multiplication_hash_function(35, table_size)) # 输出: 6
3. 中平方哈希函数(Mid-Square Hash Function)
中平方哈希函数通过将输入数据平方,然后取中间几位作为哈希值。
公式
其中,k
是输入键,p
是哈希表大小的位数。
示例代码
def mid_square_hash_function(key, table_size):
key_squared = key * key
key_str = str(key_squared)
mid = len(key_str) // 2
mid_value = int(key_str[mid-2:mid+2])
return mid_value % table_size
# 示例输入输出
table_size = 100
print(mid_square_hash_function(25, table_size)) # 输出: 56
print(mid_square_hash_function(35, table_size)) # 输出: 25
4. 折叠法哈希函数(Folding Hash Function)
折叠法哈希函数通过将输入数据分成若干部分,然后将这些部分相加得到哈希值。
公式
其中,k
是输入键,k_i
是输入键的第 i
个部分,m
是哈希表的大小。
示例代码
def folding_hash_function(key, table_size, part_size=2):
key_str = str(key)
parts = [int(key_str[i:i+part_size]) for i in range(0, len(key_str), part_size)]
return sum(parts) % table_size
# 示例输入输出
table_size = 100
print(folding_hash_function(253456, table_size)) # 输出: 66
print(folding_hash_function(356789, table_size)) # 输出: 87
5. 旋转哈希函数(Rotating Hash Function)
旋转哈希函数通过将输入数据的每一位旋转特定的位数,然后计算哈希值。
公式
其中,k
是输入键,k_i
是输入键的第 i
个字节,p
是旋转位数,m
是哈希表的大小。
示例代码
def rotating_hash_function(key, table_size, shift=4):
hash_value = 0
for byte in key.to_bytes((key.bit_length() + 7) // 8, byteorder='big'):
hash_value = ((hash_value << shift) + byte) % table_size
return hash_value
# 示例输入输出
table_size = 100
print(rotating_hash_function(253456, table_size)) # 输出: 76
print(rotating_hash_function(356789, table_size)) # 输出: 37
6. 通用哈希函数(Universal Hash Function)
通用哈希函数通过选择一个随机的哈希函数,确保不同输入键的哈希值尽可能均匀分布。
公式
其中,k
是输入键,a
和 b
是随机选择的整数,p
是一个大于 m
的质数,m
是哈希表的大小。
示例代码
import random
def universal_hash_function(key, table_size):
p = 104729 # 一个较大的质数
a = random.randint(1, p-1)
b = random.randint(0, p-1)
return ((a * key + b) % p) % table_size
# 示例输入输出
table_size = 100
print(universal_hash_function(253456, table_size)) # 输出: 42
print(universal_hash_function(356789, table_size)) # 输出: 17
以上是一些常见的哈希函数及其示例代码。每种哈希函数都有其特定的用途和优缺点。在实际应用中,选择合适的哈希函数可以显著提高哈希表的性能。希望这些示例能帮助您更好地理解和应用哈希技术。
负载因子
负载因子是哈希表中已使用位置与总位置数的比值。它用于衡量哈希表的满程度。当负载因子超过一定阈值时,通常需要对哈希表进行重新调整大小,以保持性能。
冲突
冲突是指多个键通过哈希函数映射到同一个位置的情况。冲突解决技术包括:
-
链地址法(Separate Chaining):每个哈希表位置维护一个链表,所有映射到该位置的键值对都存储在链表中。
-
开放定址法(Open Addressing):当发生冲突时,寻找下一个可用位置存储键值对。
冲突解决技术比较
冲突解决技术 | 优点 | 缺点 |
---|---|---|
链地址法 | 实现简单,适合负载因子较高的情况 | 需要额外的指针空间 |
开放定址法 | 不需要额外的指针空间 | 负载因子较高时性能下降,删除操作复杂 |
如何哈希表获得O(1)复杂度?
在理想情况下,哈希表可以实现O(1)的插入、删除和查找操作。然而,这需要一个良好的哈希函数和适当的冲突解决策略。在实际应用中,哈希表的性能通常接近O(1),但可能会因为冲突和负载因子的影响而有所下降。
不适合哈希表的问题
虽然哈希表在许多情况下都非常高效,但有些问题不适合使用哈希表。例如,需要保持键的顺序或进行范围查询的问题,哈希表就不太适用。
布隆过滤器
布隆过滤器是一种空间高效的概率型数据结构,用于测试一个元素是否是一个集合的成员。它可能会返回假阳性,但不会返回假阴性。
哈希:问题与解决方案
问题1:实现一个简单的哈希表
问题描述:实现一个简单的哈希表,支持插入、删除和查找操作。
示例代码:
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [[] for _ in range(size)]
def hash_function(self, key):
return hash(key) % self.size
def insert(self, key, value):
index = self.hash_function(key)
for item in self.table[index]:
if item[0] == key:
item[1] = value
return
self.table[index].append([key, value])
def delete(self, key):
index = self.hash_function(key)
for i, item in enumerate(self.table[index]):
if item[0] == key:
del self.table[index][i]
return
def search(self, key):
index = self.hash_function(key)
for item in self.table[index]:
if item[0] == key:
return item[1]
return None
# 示例输入输出
ht = HashTable()
ht.insert("apple", 1)
ht.insert("banana", 2)
print(ht.search("apple")) # 输出: 1
ht.delete("apple")
print(ht.search("apple")) # 输出: None
问题2:处理哈希表的冲突
问题描述:实现一个哈希表,使用链地址法处理冲突。
示例代码:
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [[] for _ in range(size)]
def hash_function(self, key):
return hash(key) % self.size
def insert(self, key, value):
index = self.hash_function(key)
bucket = self.table[index]
for i, (k, v) in enumerate(bucket):
if k == key:
bucket[i] = (key, value)
return
bucket.append((key, value))
def delete(self, key):
index = self.hash_function(key)
bucket = self.table[index]
for i, (k, v) in enumerate(bucket):
if k == key:
del bucket[i]
return
def search(self, key):
index = self.hash_function(key)
bucket = self.table[index]
for k, v in bucket:
if k == key:
return v
return None
# 示例输入输出
ht = HashTable()
ht.insert("apple", 1)
ht.insert("banana", 2)
print(ht.search("apple")) # 输出: 1
ht.delete("apple")
print(ht.search("apple")) # 输出: None
问题3:使用开放定址法处理冲突
问题描述:实现一个哈希表,使用开放定址法处理冲突。
示例代码:
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [None] * size
def hash_function(self, key):
return hash(key) % self.size
def insert(self, key, value):
index = self.hash_function(key)
while self.table[index] is not None:
if self.table[index][0] == key:
self.table[index] = (key, value)
return
index = (index + 1) % self.size
self.table[index] = (key, value)
def delete(self, key):
index = self.hash_function(key)
while self.table[index] is not None:
if self.table[index][0] == key:
self.table[index] = None
# Rehash elements to maintain open addressing
self.rehash(index)
return
index = (index + 1) % self.size
def rehash(self, start_index):
index = start_index
while True:
next_index = (index + 1) % self.size
if self.table[next_index] is None:
break
key, value = self.table[next_index]
self.table[next_index] = None
self.insert(key, value)
index = next_index
def search(self, key):
index = self.hash_function(key)
while self.table[index] is not None:
if self.table[index][0] == key:
return self.table[index][1]
index = (index + 1) % self.size
return None
# 示例输入输出
ht = HashTable()
ht.insert("apple", 1)
ht.insert("banana", 2)
print(ht.search("apple")) # 输出: 1
ht.delete("apple")
print(ht.search("apple")) # 输出: None
问题4:实现布隆过滤器
问题描述:实现一个布隆过滤器,用于测试一个元素是否是一个集合的成员。
示例代码:
import mmh3
from bitarray import bitarray
class BloomFilter:
def __init__(self, size, hash_num):
self.size = size
self.hash_num = hash_num
self.bit_array = bitarray(size)
self.bit_array.setall(0)
def add(self, item):
for i in range(self.hash_num):
digest = mmh3.hash(item, i) % self.size
self.bit_array[digest] = True
def lookup(self, item):
for i in range(self.hash_num):
digest = mmh3.hash(item, i) % self.size
if not self.bit_array[digest]:
return False
return True
# 示例输入输出
bf = BloomFilter(50, 7)
bf.add("apple")
bf.add("banana")
print(bf.lookup("apple")) # 输出: True
print(bf.lookup("orange")) # 输出: False
总结
哈希表是一种非常高效的数据结构,适用于快速查找、插入和删除操作。通过合理设计哈希函数和冲突解决策略,可以显著提高哈希表的性能。布隆过滤器则是一种空间高效的概率型数据结构,适用于测试元素是否属于某个集合。希望这些示例代码和输入输出能帮助您更好地理解和应用哈希技术。
标签:index,哈希,self,Hashing,key,table,数据结构,size From: https://blog.csdn.net/Whoisbug/article/details/145127245