数据结构-哈希表

一、哈希表的定义与核心本质

• 别称:散列表,是 键(Key)-值(Value) 映射的高效数据结构

• 核心原理:通过 哈希函数(Hash Function) 将键转换为数组索引,直接访问对应值,实现 O(1) 级别的平均查找、插入、删除效率

• 本质:用空间换时间,通过哈希函数减少查找时的比较次数,是数组查找的优化延伸

二、哈希表的核心组成

  1. 哈希函数(Hash Function)

◦ 作用:将任意类型的键(字符串、数字等)映射为固定范围的整数(数组索引)

◦ 设计要求:

◦ 确定性:同一键必须映射到同一索引

◦ 高效性:计算过程简单,耗时短

◦ 均匀性:键分布均匀,减少索引冲突

◦ 防碰撞性:尽量避免不同键映射到同一索引

◦ 常见设计方法:

◦ 直接定址法:键→索引直接对应(如键为身份证号,取后几位作为索引)

◦ 除留余数法:index = key % 数组长度(最常用,需选质数作为数组长度)

◦ 数字分析法:提取键中分布均匀的部分作为索引(如手机号后4位)

◦ 折叠法:将键拆分后叠加,取结果作为索引(如长数字拆分成多段求和)

◦ 字符串哈希:将字符ASCII码加权求和后取模(如 hash(s) = (s[0]×31ⁿ⁻¹ + s[1]×31ⁿ⁻² + ... + s[n-1]) % 数组长度)

  1. 哈希表(数组)

◦ 存储载体:连续的数组空间,每个索引位置称为 桶(Bucket),用于存储键值对

◦ 数组长度选择:通常设为质数,减少除留余数法的冲突概率

  1. 冲突解决机制

◦ 冲突定义:不同键通过哈希函数得到同一索引(无法避免,只能缓解)

◦ 开放寻址法(闭散列):

◦ 核心:冲突时,按某种规则在数组中寻找下一个空闲桶

◦ 常见方式:

◦ 线性探测:冲突后依次检查下一个索引(index+1, index+2...,易产生聚集)

◦ 二次探测:冲突后按索引±1²、±2²...查找(减少聚集,需数组长度为4k+3型质数)

◦ 双重哈希:用第二个哈希函数计算步长,步长=hash2(key)(分散性最好,避免聚集)

◦ 缺点:数组满时无法插入,删除需标记"已删除"(避免影响后续查找)

◦ 链地址法(开散列):

◦ 核心:每个桶对应一个链表(或红黑树),冲突的键值对依次存入链表中

◦ 优点:冲突处理简单,数组利用率高,删除方便(直接删除链表节点)

◦ 优化:当链表长度超过阈值(如8),转为红黑树(Java HashMap的实现方式),提升查询效率

◦ 其他方法:

◦ 再哈希法:冲突时调用另一个哈希函数重新计算索引

◦ 公共溢出区:将所有冲突的键值对存入单独的溢出数组

三、哈希表的关键性能指标

• 负载因子(Load Factor):α = 存储的键值对数量 / 数组长度

◦ 意义:衡量哈希表的拥挤程度,α越小,冲突概率越低,效率越高

◦ 阈值:通常α阈值设为0.75(Java HashMap默认),超过则触发 扩容(Resizing)

• 扩容机制:

◦ 过程:创建新的更大数组(通常是原长度的2倍,且为质数),重新计算所有键的哈希值并迁移到新数组

◦ 目的:降低负载因子,减少后续冲突,保证效率稳定

四、哈希表的优缺点

  1. 优点:

◦ 平均时间复杂度:查找、插入、删除均为 O(1),效率远超数组、链表、树

◦ 键值对映射直接,无需遍历,适合高频查找场景

  1. 缺点:

◦ 最坏时间复杂度:O(n)(哈希函数极差或负载因子过高,导致所有键冲突,链表长度过长)

◦ 空间利用率较低(需预留扩容空间,开放寻址法浪费更多空间)

◦ 无序存储:键值对无固定顺序,无法按索引有序访问

◦ 哈希函数设计难度高:需兼顾均匀性、高效性,否则影响性能

五、常见应用场景

• 数据缓存:如Redis、浏览器缓存(键为URL,值为缓存内容)

• 快速查找:字典查询、用户信息查询(键为用户名/ID,值为用户数据)

• 去重操作:如日志去重、数组去重(利用键的唯一性)

• 键值对存储:数据库索引、哈希映射(HashMap)、哈希集合(HashSet,仅存键)

相关推荐
忧郁的Mr.Li9 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
yq1982043011569 小时前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class9 小时前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
有位神秘人9 小时前
kotlin与Java中的单例模式总结
java·单例模式·kotlin
golang学习记9 小时前
IntelliJ IDEA 2025.3 重磅发布:K2 模式全面接管 Kotlin —— 告别 K1,性能飙升 40%!
java·kotlin·intellij-idea
爬山算法9 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
java·压力测试·hibernate
驭渊的小故事10 小时前
简单模板笔记
数据结构·笔记·算法
消失的旧时光-194310 小时前
第十四课:Redis 在后端到底扮演什么角色?——缓存模型全景图
java·redis·缓存
BD_Marathon10 小时前
设计模式——依赖倒转原则
java·开发语言·设计模式
VT.馒头10 小时前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展