如何用Python实现哈希表

哈希表简介

哈希表,在计算机科学中,是一种通过多对一的映射将键映射到值的数据结构。Hash表通过使用散列函数将每个键映射到一个值上,这个值通常是一个桶号,桶里存储了与这个键对应的值。

一般而言,哈希表的实现需要以下几个步骤:

设计哈希函数,将键转换成键的哈希码;

将哈希码映射到特定的桶上;

处理冲突的方法,如开放定地址法、链接法等;

设计哈希表

1.实现哈希函数

哈希函数的作用是将任意长度的输入(比如字符串)压缩成某个固定长度的输出,通常称为哈希值(hash value)或哈希码(hash code)。

在 Python 中,我们常用的哈希函数有以下几种:

SHA1 加密算法:实现方式比较简单,但安全性相对不够高;

MD5 加密算法:相对于 SHA1 更加安全,但加密效率相对低一些;

CRC32 算法:为了提供高速数据校验而广泛使用的算法,但有很大的哈希冲突。

下面是一段使用 Python 实现哈希函数的代码:

import hashlib

def hash_func(key):

md5 = hashlib.md5()

md5.update(key.encode('utf-8'))

return int(md5.hexdigest(), 16)

上面的代码中,我们选择了 MD5 算法作为哈希函数,并用字符串类型的键计算哈希值,结果返回一个整数类型的哈希码。

2.实现桶

哈希表需要用数组来存储数据,每个元素叫做一个桶(bucket),每个桶可以存放一个或多个键值对。桶的个数通常选为一个合适的质数,可以通过哈希函数将键映射到一个桶中。

下面是一个 Python 实现的哈希表对象:

from typing import List

class HashTable:

def __init__(self, capacity: int = 11):

self.capacity = capacity

self.buckets: List[None] = [None] * self.capacity

# 哈希函数

def hash(self, key: str) -> int:

return hash(key) % self.capacity

# 插入数据

def insert(self, key: str, value: any) -> None:

index = self.hash(key)

if self.buckets[index] is None:

self.buckets[index] = []

self.buckets[index].append((key, value))

# 查询数据

def search(self, key: str) -> any:

index = self.hash(key)

bucket = self.buckets[index]

if bucket is None:

return None

for (k, v) in bucket:

if k == key:

return v

return None

上面的代码中,我们使用了 Python 中的 List 类来存储桶,使用哈希函数将键转换成相应的桶号,然后在相应的桶中进行插入和查询操作。如果桶中已经存在相同键值对,则需要将其添加在哈希冲突的链表中。

3.解决冲突

哈希函数的设计不能避免哈希冲突,因此,解决哈希冲突的方法是哈希表的一个重要组成部分。

常见的解决哈希冲突的方法有以下两种:

(1)线性探测法

线性探测法,也称为开放地址法,是解决哈希冲突的一种方式。当发生哈希冲突时,线性探测法会尝试在哈希表中的下一个空桶中插入数据,直到找到一个空桶为止。这种方法在处理哈希表中冲突较少的情况下比较有效,但在处理冲突较多的情况下效率会变得很低。

下面是一个使用线性探测法解决哈希冲突的 Python 实现:

class HashTable:

def __init__(self, capacity: int = 11):

self.capacity = capacity

self.buckets: List[None] = [None] * self.capacity

# 哈希函数

def hash(self, key: str) -> int:

return hash(key) % self.capacity

# 插入数据

def insert(self, key: str, value: any) -> None:

index = self.hash(key)

if self.buckets[index] is None:

self.buckets[index] = (key, value)

else:

while self.buckets[index] is not None and self.buckets[index][0] != key:

index = (index + 1) % self.capacity

self.buckets[index] = (key, value)

# 查询数据

def search(self, key: str) -> any:

index = self.hash(key)

while self.buckets[index] is not None:

if self.buckets[index][0] == key:

return self.buckets[index][1]

index = (index + 1) % self.capacity

return None

上面的代码中,我们使用了 while 循环解决了哈希冲突问题,当检测到桶已被占用,则算法会尝试在下一个桶中插入数据,直到找到一个空桶或者找到相同的 key 就会停止,这就是线性探测法的基本原理。

(2)链地址法

链地址法,又称为拉链法,是一种常见的处理哈希冲突的方法,用链表来存储哈希表中的所有元素,一旦出现冲突,就直接将元素插入到链表中。

下面是一个使用链地址法解决哈希冲突的 Python 实现:

class HashTable:

def __init__(self, capacity: int = 11):

self.capacity = capacity

self.buckets: List[None] = [None] * self.capacity

# 哈希函数

def hash(self, key: str) -> int:

return hash(key) % self.capacity

# 插入数据

def insert(self, key: str, value: any) -> None:

index = self.hash(key)

if self.buckets[index] is None:

self.buckets[index] = []

self.buckets[index].append((key, value))

# 查询数据

def search(self, key: str) -> any:

index = self.hash(key)

bucket = self.buckets[index]

if bucket is None:

return None

for (k, v) in bucket:

if k == key:

return v

return None

上面的代码中,我们使用了 Python 中的 List 类来存储桶,使用哈希函数将键转换成相应的桶号,然后在相应的桶中进行插入和查询操作。如果桶中已经存在相同键值对,则需要将其添加在哈希冲突的链表中。

使用哈希表

1.插入数据

使用哈希表的常见操作是插入数据,代码示例如下:

hash_table = HashTable()

hash_table.insert('键1', '值1')

hash_table.insert('键2', '值2')

hash_table.insert('键3', '值3')

2.查询数据

使用哈希表查询数据的操作很简单,代码示例如下:

hash_table = HashTable()

hash_table.insert('键1', '值1')

hash_table.insert('键2', '值2')

hash_table.insert('键3', '值3')

value = hash_table.search('键2') # 查找键2对应的值

print(value)

上述程序会输出:

'值2'

总结

哈希表是一种非常高效的数据结构,在 Python 中也有很多实现方法,本文介绍了一个基于数组和 Python List 实现的散列表,并讲述了常见的哈希函数和处理哈希冲突的方法。在实际应用中,我们可以根据具体的条件和需求来选择相应的实现方法。

后端开发标签