码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 哈希表的概念


    文章目录

    • 哈希表的基本概念
      • 哈希表的定义
      • 哈希函数
      • 哈希冲突
        • 链地址法
        • 开放寻址法
    • Java 中的哈希表和哈希集合
      • Map \texttt{Map} Map、 HashMap \texttt{HashMap} HashMap 和 TreeMap \texttt{TreeMap} TreeMap
      • Set \texttt{Set} Set、 HashSet \texttt{HashSet} HashSet 和 TreeSet \texttt{TreeSet} TreeSet
    • 哈希表的应用场景和使用技巧
    • 目录

    哈希表的基本概念

    哈希表的定义

    哈希表也称散列表,是一种以键值对形式存储记录的数据结构,该数据结构支持根据键的内容直接访问在内存特定位置的值,并且可以进行查找、添加和删除操作。

    哈希表的原理是将键通过函数映射到内存特定位置,加快操作速度,该函数称为哈希函数或散列函数。

    理想情况下,哈希表的每次操作的时间复杂度是 O ( 1 ) O(1) O(1)。

    哈希函数

    哈希函数的作用是将键映射到索引。哈希函数的设计很重要,决定了哈希表的时间与空间的平衡。

    哈希函数应满足容易计算且能够将所有的键均匀分布。理想情况下,不同的键应该映射到不同的索引,但是实际情况下,因为哈希函数的设计和索引空间限制等因素,可能出现不同的键映射到相同的索引的情况,称为哈希冲突。

    哈希冲突

    不同的键映射到相同的索引称为哈希冲突。哈希冲突的概率和哈希函数的设计有直接关系,好的哈希函数可以显著减少哈希冲突的情况,但是很难完全避免哈希冲突。因此,需要有解决哈希冲突的方法。

    有两种常见的解决哈希冲突的方法,一是链地址法,二是开放寻址法。

    链地址法

    链地址法也称拉链法,其思想是将映射到相同索引的值存入同一个链表中。

    链地址法解决哈希冲突的做法简单,查找、添加和删除操作的实现都较为简单,虽然链地址法的各项操作的时间复杂度在最坏情况下会退化为线性,但是在平均情况下仍然是 O ( 1 ) O(1) O(1)。

    开放寻址法

    开放寻址法的思想是:当发生哈希冲突时,继续探查哈希表中的其他索引位置,直到找到空的索引位置用于填入值。

    和链地址法相比,开放寻址法的删除操作更为复杂。为了避免在删除之后导致重新寻址的键无法找到,进行删除操作时,只能在被删除的位置做删除标记而不能真正删除。

    开放寻址法的常用探查做法有三种:线性探查、二次探查和双重哈希。

    假设哈希表有 m m m 个槽位,定义辅助哈希函数 h h h,其值域为从 0 0 0 到 m − 1 m - 1 m−1 的全部整数。

    线性探查采用的哈希函数是 f ( x , i ) = ( h ( x ) + i )   m o d   m f(x, i) = (h(x) + i) \bmod m f(x,i)=(h(x)+i)modm,其中 i i i 是整数且 0 ≤ i < m 0 \le i < m 0≤i<m。线性探查从特定槽位开始依次遍历每个槽位,直到找到未被占用的槽位。线性探查的实现简单,但是当被占用的槽位增加时,线性探查的平均探查时间会增加,导致性能受到影响。

    二次探查采用的哈希函数是 f ( x , i ) = ( h ( x ) + c 1 × i + c 2 × i 2 )   m o d   m f(x, i) = (h(x) + c_1 \times i + c_2 \times i^2) \bmod m f(x,i)=(h(x)+c1​×i+c2​×i2)modm,其中 i i i 是整数且 0 ≤ i < m 0 \le i < m 0≤i<m, c 1 c_1 c1​ 和 c 2 c_2 c2​ 是正的辅助常数。二次探查的效果优于线性探查,但是效果取决于 c 1 c_1 c1​、 c 2 c_2 c2​ 和 m m m 的取值。

    双重哈希采用的哈希函数是 f ( x , i ) = ( h 1 ( x ) + h 2 ( x ) × i )   m o d   m f(x, i) = (h_1(x) + h_2(x) \times i) \bmod m f(x,i)=(h1​(x)+h2​(x)×i)modm,其中 h 1 h_1 h1​ 和 h 2 h_2 h2​ 是两个辅助哈希函数, i i i 是整数且 0 ≤ i < m 0 \le i < m 0≤i<m。为了探查到整个哈希表, h 2 ( x ) h_2(x) h2​(x) 的值必须和 m m m 互质。

    三种探查做法中,双重哈希的效果最好。

    Java 中的哈希表和哈希集合

    Map \texttt{Map} Map、 HashMap \texttt{HashMap} HashMap 和 TreeMap \texttt{TreeMap} TreeMap

    Map \texttt{Map} Map 接口表示用于存储键值对映射关系的数据结构,其中的任意两个键值对的键都不相同。 Map \texttt{Map} Map 接口需要通过实现类实例化,常见的实现类包括 HashMap \texttt{HashMap} HashMap 类和 TreeMap \texttt{TreeMap} TreeMap 类。

    HashMap \texttt{HashMap} HashMap 类是哈希映射的类,其底层实现是数组。解决哈希冲突的方法是链地址法,具体做法在不同 JDK 版本中有所不同。

    • 在 JDK 1.7 及之前的版本中, HashMap \texttt{HashMap} HashMap 类的实现为数组 + 单向链表,即数组中的每个元素都是一个单向链表。
    • 在 JDK 1.8 及之后的版本中, HashMap \texttt{HashMap} HashMap 类的实现为数组 + 单向链表 + 红黑树,当链表长度过大时,将链表转化为红黑树以提升效率。链表和红黑树的切换规则如下:
      • 如果链表的长度大于 8 8 8,则将链表转化为红黑树;
      • 如果红黑树的结点数小于 6 6 6,则将红黑树转化回链表。

    HashMap \texttt{HashMap} HashMap 的各项操作的平均时间复杂度是 O ( 1 ) O(1) O(1),但是其中的键值对是无序的。

    TreeMap \texttt{TreeMap} TreeMap 类是有序映射的类,其底层实现是红黑树。 TreeMap \texttt{TreeMap} TreeMap 类可以确保其中的键值对是按照键的大小排序的。由于需要维护有序性, TreeMap \texttt{TreeMap} TreeMap 的各项操作的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是 TreeMap \texttt{TreeMap} TreeMap 存储的键值对数目。

    HashMap \texttt{HashMap} HashMap 在 JDK 1.8 及之后版本的实现和 TreeMap \texttt{TreeMap} TreeMap 的实现都使用了红黑树的数据结构。红黑树是一种自平衡二叉搜索树,对于 n n n 个结点的红黑树,其查找、插入和删除操作的时间复杂度都是 O ( log ⁡ n ) O(\log n) O(logn)。红黑树将在树、二叉树和二叉搜索树的部分介绍。

    Set \texttt{Set} Set、 HashSet \texttt{HashSet} HashSet 和 TreeSet \texttt{TreeSet} TreeSet

    Set \texttt{Set} Set 接口表示用于存储无重复元素的数据结构,其中的任意两个元素都不相同,同一个元素不能被重复加入。 Set \texttt{Set} Set 接口需要通过实现类实例化,常见的实现类包括 HashSet \texttt{HashSet} HashSet 类和 TreeSet \texttt{TreeSet} TreeSet 类。

    HashSet \texttt{HashSet} HashSet 类是哈希集合的类,其实现基于 HashMap \texttt{HashMap} HashMap 类。 HashSet \texttt{HashSet} HashSet 的各项操作的平均时间复杂度是 O ( 1 ) O(1) O(1),但是其中的元素是无序的。

    TreeSet \texttt{TreeSet} TreeSet 类是有序集合的类,其实现基于 TreeMap \texttt{TreeMap} TreeMap 类。 TreeSet \texttt{TreeSet} TreeSet 中的元素是有序的,各项操作的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是 TreeSet \texttt{TreeSet} TreeSet 存储的元素数目。

    哈希表的应用场景和使用技巧

    哈希表的应用主要有以下场景:

    1. 计数,使用哈希表记录元素的次数;
    2. 存储已经计算过的信息,避免重复计算,在动态规划中经常会使用哈希表存储已经计算过的信息;
    3. 判断一个元素是否已经访问过,该场景常用哈希集合,判断链表是否有环问题和搜索问题中经常使用哈希集合;
    4. 和双向链表结合,可以实现最近最少使用(LRU)缓存和最不经常使用(LFU)缓存。

    大多数情况下,使用哈希表和哈希集合的场景都会使用 HashMap \texttt{HashMap} HashMap 类和 HashSet \texttt{HashSet} HashSet 类的对象。如果哈希表的键范围有限或者哈希集合的元素范围有限,例如只能是数字或者只能是字母,则可以用数组代替哈希表。虽然从复杂度分析的角度而言,数组和哈希表的时间复杂度和空间复杂度相同,但是在实际运行时,数组的操作时间和占用空间都优于哈希表。

    目录

    1. 哈希表题目:两数之和
    2. 哈希表题目:唯一摩尔斯密码词
    3. 哈希表题目:快乐数
    4. 哈希表题目:在长度 2N 的数组中找出重复 N 次的元素
    5. 哈希表题目:有效的数独
    6. 哈希表题目:键盘行
    7. 哈希表题目:宝石与石头
    8. 哈希表题目:赎金信
    9. 哈希表题目:“气球”的最大数量
    10. 哈希表题目:相交链表
    11. 哈希表题目:环形链表
    12. 哈希表题目:环形链表 II
    13. 哈希表题目:整数转罗马数字
    14. 哈希表题目:罗马数字转整数
    15. 哈希表题目:同构字符串
    16. 哈希表题目:单词规律
    17. 哈希表题目:查找和替换模式
    18. 哈希表题目:字符串中的第一个唯一字符
    19. 哈希表题目:公平的糖果交换
    20. 哈希表题目:找出数组中的幸运数
    21. 哈希表题目:独特的电子邮件地址
    22. 哈希表题目:验证外星语词典
    23. 哈希表题目:查找共用字符
    24. 哈希表题目:两个相同字符之间的最长子字符串
    25. 哈希表题目:最短补全词
    26. 哈希表题目:亲密字符串
    27. 哈希表题目:判断路径是否相交
    28. 哈希表题目:矩阵置零
    29. 哈希表题目:设计哈希集合
    30. 哈希表题目:设计哈希映射
    31. 哈希表题目:从英文中重建数字
    32. 哈希表题目:猜数字游戏(猜数字游戏策略实现)
    33. 哈希表题目:数组中的 k-diff 数对
    34. 哈希表题目:数组的度
    35. 哈希表题目:单词子集
    36. 哈希表题目:子域名访问计数
    37. 哈希表题目:字母板上的路径
    38. 哈希表题目:制造字母异位词的最小步骤数
    39. 哈希表题目:数的平方等于两数乘积的方法数
    40. 哈希表题目:砖墙
    41. 哈希表题目:森林中的兔子
    42. 哈希表题目:保证文件名唯一
    43. 哈希表题目:最长连续序列
    44. 哈希表题目:在系统中查找重复文件
    45. 哈希表题目:数组中重复的数据
    46. 哈希表题目:四数相加 II
    47. 哈希表题目:网格照明
    48. 哈希表题目:从链表中删去总和值为零的连续结点
    49. 哈希表题目:设计地铁系统
    50. 哈希表题目:缺失的第一个正数
    51. 哈希表题目:元音拼写检查器
    52. 哈希表题目:TinyURL 的加密与解密
    53. 哈希表题目:最大频率栈
    54. 哈希表题目:设计推特
    55. 哈希表题目:LRU 缓存
    56. 哈希表题目:最大相等频率
    57. 哈希表题目:原子的数量
    58. 哈希表题目:LFU 缓存
    59. 哈希表题目:Lisp 语法解析
    60. 哈希表题目:全 O(1) 的数据结构
  • 相关阅读:
    2023年天津中德应用技术大学专升本机械电子工程专业考试大纲
    C#获取指定软件安装路径
    【CSS】自定义下拉框
    记一次 .NET 某RFID标签管理系统 CPU 暴涨分析
    正点原子嵌入式linux驱动开发——Linux内核定时器
    Spring 自定义事件,通过注解的方式来实现事件监听
    SpringBoot整合Mybatis-Plus
    Python基础知识从hello world 开始(第一天)
    6.19作业
    自动化搭建初期必要的需求分析
  • 原文地址:https://blog.csdn.net/stormsunshine/article/details/121449622
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号