为什么要用散列表(哈希表,hashtable)

本文探讨了哈希表的基本概念及其应用场景,介绍了哈希表如何通过映射算法实现高效的数据存储与检索,并讨论了映射过程中可能出现的问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Hash表不是直接直接把关键字作为数组下标,而是根据关键字计算出下标。-------算法导论

       数组是一种支持直接访问的数据结构,使用确定的位置来存储和检索数据,十分高效。对数组操作时,隐含的一个动作是通过 特定的规律来确定下标。例如一张图片像素所组成的二维数组中,通过行数a和列数b可以得到二维下标(a-1,b-1)。

       大家对于数列都很熟悉,等比数列,等差数列等都是常用到的数列形式。数组下标本身就是一种数列,只不过这是最简单的顺序递增:“1,2,3,4…”。有一类智力题,是看序列找规律,预测下一个,从逻辑的角度来讲,只要你能自圆其说下一个是什么都有可能(有兴趣可以去看看-电影-牛津大学谋杀案)。而一组随机给出的数列起始位,也是可以预测之后数列的发展,只不过其中的规律特别复杂罢了。实际上寻找的是特征到位置的映射关系

      如果给出一个输入值的集合,包含很多元素,能否按照各个元素的特征找到映射规律使其存放到顺序(存储位置顺序)的数组中呢?Hashtable就是用来解决这个问题的。

      对于集合Arr = {23,7,9,1,10},如何才能建立一个快速的映射关系呢?最简单的方式就是取大小为max(Arr(0),Arr(1),…Arr(n))一个数组,将其存储到对应的位置上,隐含的操作是value mod max(valueSet),为了存储五个元素,花费了23个存储位置,这显然有些浪费。可不可以让它们的存储得更紧凑一些呢,可以使用mod运算很方便的达到这一点:使用value mod 5,将得到{23,7,9,1,10} -> {3,2,4,1,0},这样我们就可以使用5个位置来存储了。当然数组是我特意挑选的,mod也不是唯一的映射方法,这里我们可以看到散列的一个优点将一个大的输入值域,映射到小的输出值域上来,节省空间。

       另外一种适用Hashmap的场景是,不能直接获得下标值的情况。例如集合中的元素是文件或者字符串,通过映射,获取hash值确定存储位置(同时达到映射到小集合的目的)。

                         

图片上传

        Hash映射的关系如图所示,注意以下几点:

  1. 在完成hash映射之前的集合A的值域一般大于和等于集合B的值域的,但是并不强制。
  2. 集合A本身的元素个数并不等于其值域,一般要进行预去重处理。
  3. 集合A的值域有可能是有限的也有可能是无限的,例如正有理数->向下取整->整数。
  4. 集合B必定有有限的。
  5. Hashmap一般只支持Insert,Search,Delete这三种操作,因此十分高效,操作的时间复杂度为O(1)。

实际的场景要复杂一点,大集合到小集合的映射是有碰撞的风险的。怎样降低这样的风险?碰撞发生了怎样处理呢?后续的章节会讨论着两个问题。

散列表哈希表是两种常用于高效数据存储与查询的数据结构,它们在许多应用场景中被广泛使用。尽管两者有相似之处,但也存在一些关键区别。 散列表是一种基于数组实现的数据结构,它通过一个称为“散列函数”的映射函数将键(key)转换为数组中的索引位置,从而实现对值(value)的快速访问。为了处理多个键映射到同一索引位置的情况,即哈希冲突,散列表通常采用链式存储或开放地址法来解决这一问题。链式存储意味着每个数组元素实际上是一个链表头节点,当发生冲突时,新的键值对会被添加到该链表中;而开放地址法则是在遇到冲突时寻找下一个可用的位置插入新条目[^1]。 哈希表则是另一种形式的散列表,它同样依赖于哈希函数来完成键到索引的转换,并且也支持高效的插入、删除和查找操作。然而,哈希表更强调其作为存储键值对集合的功能,其中每个键都唯一地关联着一个值。哈希表内部通常也是利用链表或者开放寻址等技术来应对哈希碰撞的问题。此外,哈希表对于哈希函数的设计有着更高的要求,以确保分布均匀并减少冲突的可能性[^2]。 值得注意的是,在实际应用中,“哈希表”与“散列表”这两个术语经常互换使用,尤其是在讨论具体实现细节时。例如,在Java编程语言中提到的`HashMap`就是一种典型的哈希表实现方式,它结合了对象数组加链表的形式来优化性能,同时默认初始化容量设置为16,以及负载因子控制着哈希桶的最大利用率[^3]。 综上所述,虽然散列表哈希表都提供了平均情况下接近O(1)时间复杂度的查找效率,但它们侧重点不同:散列表更多关注如何有效组织数据以便于检索;而哈希表则侧重于构建键值之间的映射关系。因此,选择哪种结构取决于特定应用场景下的需求。 ```python # 示例代码 - 简单的哈希表实现 class HashTable: def __init__(self, size=16): self.size = size self.table = [[] for _ in range(size)] # 使用列表推导创建空列表组成的列表 def _hash_function(self, key): return hash(key) % self.size # 哈希函数计算索引 def put(self, key, value): index = self._hash_function(key) bucket = self.table[index] found = False for i, (k, v) in enumerate(bucket): if k == key: bucket[i] = (key, value) # 更新已存在的键 found = True break if not found: bucket.append((key, value)) # 添加新的键值对 def get(self, key): index = self._hash_function(key) bucket = self.table[index] for k, v in bucket: if k == key: return v # 返回找到的值 raise KeyError(f"Key {key} not found") # 键不存在时抛出异常 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值