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

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)。

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

### 哈希表散列表)的数据结构原理 哈希表是一种支持快速查找操作的数据结构,在理想条件下,其时间复杂度为 O(1)[^2]。这种高效性能源于哈希表内部采用了一种称为哈希函数的技术来映射键到数组中的位置。 #### 哈希表的概念 哈希表利用一种特殊的方式存储数据——是按照顺序排列而是依据特定算法计算得到的位置放置元素。这种方式使得即使面对大量数据也能保持高效的存取速度[^1]。 #### 哈希函数设计原则 为了使哈希表能够正常工作并发挥最佳效能,需要精心设计哈希函数: - **均匀分布**:好的哈希函数应该尽可能让同的输入产生相同的输出值; - **简单快捷**:由于每次访问都要执行该函数,因此它应当具有较高的运算效率; - **减少冲突**:尽管完全消除碰撞是可能的任务,但仍需努力降低发生概率; 一些常见的哈希函数构建方式包括但限于直接定址法、除留余数法、平方取中法以及基数转换法等[^3]。 ```cpp // C++ 中简单的哈希函数示例 (除留余数法) int hashFunction(int key, int tableSize){ return key % tableSize; } ``` #### 处理哈希冲突的方法 当两个同关键字经过相同哈希函数变换后指向同一索引时就会引发所谓的“哈希冲突”。针对这种情况有两种主要解决方案: ##### 开放地址法 这种方法是在遇到冲突时创建新的节点链接起来形成链表,而是在原数组里寻找下一个可用槽位继续尝试插入直到成功为止。具体策略有线性探测、二次探查等多种变体形式。 ##### 链地址法(拉链法) 每当检测到已有记录占据目标位置,则新建一个单向链表连接这些溢出项。此方案相对容易实现且易于维护,但可能增加额外空间消耗。 ```cpp struct ListNode { int val; struct ListNode *next; }; class MyHashTable { public: vector<ListNode*> buckets; void insert(int value); }; ``` 对于 Java 的 `HashMap` 类而言,除了上述提到的基础机制外还涉及到了对象比较逻辑。每当涉及到新 Entry 加入或者旧 Entry 查找更新的时候都会先调用 `hashCode()` 方法获取对应 bucket 序号再进一步确认是否真的存在重复 Key 则会触发 `equals()` 进行最终判断[^4]。 ### 实际应用案例分析 在实际编程实践中,开发者可以根据需求选择合适的库或框架所提供的现成容器类型如 STL 中的 unordered_map 或者手写简易版自定义 HashTable 来满足项目所需功能特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值