《掰开揉碎讲编程-长篇》一文读懂 哈希表

news/2025/10/20 9:15:35/文章来源:https://www.cnblogs.com/Qiuner/p/19151776

img

博主粉丝群介绍: ① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。
② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。
③ 群内也有职场精英,大厂大佬,可交流技术、面试、找工作的经验。
进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬。
进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。
群公告里还有全网大赛and约稿汇总/博客提效工具集/CSDN自动化运营脚本 有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。
  • 刷算法题的时候,别人的题解总是:"加个哈希表,O(n)秒了"。而你还在纠结:哈希表是什么?为什么能加速?怎么用? 这篇文章就是来解答这些问题的。

前置知识要求

知识点 重要程度 说明
数组基础 必需 ⭐ 需要了解数组的基本操作和时间复杂度
时间复杂度 必需 ⭐ 需要理解 O(1)、O(n) 等概念
基础编程语法 推荐 会使用一门编程语言即可
链表 加分项 了解更佳,不了解也不影响
  • 必需:没有这个基础很难理解本文
  • 推荐:有这个基础学习更轻松
  • 加分项:了解更好,不了解也不影响

为什么叫 Hash?

Hash 的英文原意就是"切碎、混杂"。想象你在炖一锅大杂烩(hash):胡萝卜、土豆、红肉、白肉……很多复杂叫不出名的食材混合在一起煮,最后成了一锅汤。

这个过程就是哈希。

原本各有特点的食材(数据),经过烹煮(哈希函数),变成了一锅融合的汤(Hash 值)。

哈希的本质:把不能当索引的东西(字符串、对象)变成能当索引的数字,但对外呈现为键值对,更符合人类思维。

和数组有什么关系?

数组是怎么组织数据的?

通过序号(索引),像门牌号一样:

数组:[苹果, 芒果, 葡萄, 草莓, 西瓜]
索引:  0    1    2    3    4

问题来了:我想找到"葡萄"要怎么找?

方法 1:暴力查找(遍历挨个比对)

第1步:看索引0 → "苹果"  不是
第2步:看索引1 → "芒果"  不是  
第3步:看索引2 → "葡萄"  找到了!

时间复杂度:O(n) —— 最坏情况要找 n 次

但如果我知道索引呢?

直接访问:数组[2] → "葡萄"  一次搞定!

时间复杂度:O(1) —— 直达,不用挨个找

矛盾点

  • 问题:我现在有"葡萄"这个名字,但不知道它在索引几
  • 理想状态:能不能通过"葡萄"这个名字,直接算出它在索引 2?

哈希表就是来解决这个问题的!

哈希表的做法

用哈希函数把"葡萄"变成索引

第一步:设计一个哈希函数(就像炖汤的配方)
hash("葡萄") = 2  ← 把"葡萄"这个字符串"煮"成数字2
​
第二步:存储时
"葡萄" → hash("葡萄") = 2 → 存到数组[2]的位置
​
第三步:查找时
想找"葡萄"?
→ hash("葡萄") = 2  
→ 直接去数组[2]拿  
→ 时间复杂度:O(1)!

对比总结

查找方式 普通数组遍历 哈希表
操作 从头到尾挨个比对 通过哈希函数直接定位
过程 苹果 → 芒果 → 葡萄 hash("葡萄")=2 → 数组[2]
时间复杂度 O(n) O(1)
特性 普通数组 哈希表(基于数组)
底层结构 数组 数组
索引是什么 必须是连续的数字 0,1,2,3... 任意类型(通过哈希函数转成数字)
存储方式 arr[0] = value hash("key")→ 索引 →arr[索引] = value
查找速度 O(1)(知道索引)不知道 O(n) O(1) 平均 / O(n) 最坏

哈希表在代码中长什么样?(Java)

在 Java 中,哈希表的表现形式为键值对(Key-Value)

// Java 中的 HashMap
HashMap<String, String> fruitMap = new HashMap<>();
​
// 存储:键 → 值
fruitMap.put("apple", "苹果");
fruitMap.put("grape", "葡萄");
fruitMap.put("mango", "芒果");
​
// 查找:通过键直接拿到值
String result = fruitMap.get("grape");  // 返回 "葡萄"

键值对是什么?

就像字典一样:

英文(Key 键)    中文(Value 值)
─────────────────────────────
apple       →      苹果
grape       →      葡萄  
mango       →      芒果
  • Key(键):用来查找的"钥匙",比如 "apple"
  • Value(值):存储的实际数据,比如 "苹果"

底层怎么存的?

还是用数组!

用户看到的(键值对形式):
"apple" → "苹果"
"grape" → "葡萄"
​
底层实际存储(数组):
[  null,  null,  "葡萄",  "苹果",  null  ]0      1       2        3       4↑        ↑hash("grape")  hash("apple")= 2           = 3

过程:

  1. 你存入 "apple" → "苹果"
  2. 哈希函数计算:hash("apple") = 3
  3. 把 "苹果" 存到数组的 [3] 位置
  4. 查找时:hash("apple") = 3,直接去 [3]
  • 因为封装,我们可以不关注具体实现,实现也不是本文的重点,我们已经明白了哈希,接下来将讲在算法中如何使用哈希

哈希表中常用的方法有哪几个?

方法 描述 示例代码
put(K key, V value) HashMap 中添加一个键值对,如果键已存在,则覆盖原有值。 map.put("key1", "value1");
get(Object key) 根据给定的键返回对应的值,如果键不存在,则返回 null String value = map.get("key1");
containsKey(Object key) 检查 HashMap 中是否包含指定的键。 boolean contains = map.containsKey("key1");
containsValue(Object value) 检查 HashMap 中是否包含指定的值。 boolean contains = map.containsValue("value1");
remove(Object key) 删除指定键的键值对,返回该键的值,如果键不存在则返回 null map.remove("key1");
clear() 清空 HashMap 中的所有键值对。 map.clear();
size() 返回 HashMap 中键值对的数量。 int size = map.size();
isEmpty() 判断 HashMap 是否为空。 boolean isEmpty = map.isEmpty();
keySet() 返回 HashMap 中所有键的集合。 Set keys = map.keySet();
values() 返回 HashMap 中所有值的集合。 Collection values = map.values();
entrySet() 返回 HashMap 中所有键值对的集合。 Set> entrySet = map.entrySet();
getOrDefault 根据键 key 获取对应的值,如果键不存在,则将值变为 defaultValue map.getOrDefault("key1", "defaultValue");

实战:刷 LeetCode 时怎么用哈希表得到更好的时间复杂度?

简单题:难度 1

1512. 好数对的数目

给你一个整数数组 nums 。如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。返回好数对的数目。示例 1:输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始
示例 2:输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对
示例 3:输入:nums = [1,2,3]
输出:0提示:1 <= nums.length <= 100
1 <= nums[i] <= 100
  • 算法小白,第一眼看过去肯定是用双重循环暴力破解,但咱们已经学过哈希了,应该能想到这道题要用哈希来做。

答案

image-20251017092209121

class Solution {// 定义方法,输入为整数数组,返回满足条件的配对数量public int numIdenticalPairs(int[] nums) {// 创建一个 HashMap 来存储每个数字出现的次数HashMap<Integer, Integer> hashMap = new HashMap<>();int count = 0; // 用来记录符合条件的配对数// 遍历数组中的每个元素for (int i = 0; i < nums.length; i++) {// 如果该数字已经出现在哈希表中,累加已有的配对数if (hashMap.get(nums[i]) != null) {count += hashMap.get(nums[i]);}// 更新该数字在哈希表中的出现次数if (hashMap.containsKey(nums[i])) {hashMap.put(nums[i], hashMap.get(nums[i]) + 1);} else {hashMap.put(nums[i], 1);}}return count; // 返回符合条件的配对数}
}

image-20251017092627733

通用小技巧

不知道你有没有一个感觉,觉得我文中的代码是可以化简的

如果你没有这种感觉,那我建议回过头去看 哈希表中常用的方法有哪几个? 这一小结内容

if (hashMap.containsKey(nums[i])) {hashMap.put(nums[i], hashMap.get(nums[i]) + 1);
} else {hashMap.put(nums[i], 1);
}

分割线

分割线,你可以自己想想

  • 是的,可以变成这种一行的形式
hashMap.put(nums[i], hashMap.getOrDefault(nums[i], 0) + 1);

简单题:难度 2

389. 找不同

给定两个字符串 s 和 t ,它们只包含小写字母。字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。请找出在 t 中被添加的字母。
  • 也很简单,先把 s 出现过的字母全部 put 到哈希表里面去,然后再来个循环

答案

image-20251017092233937

import java.util.HashMap;class Solution {public char findTheDifference(String s, String t) {HashMap<Character,Integer> hash = new HashMap();for(int i=0;i<s.length();i++){char ch = s.charAt(i);if (hash.containsKey(ch)) {hash.put(ch, hash.get(ch) + 1);} else {hash.put(ch, 1);}}for(int i=0;i<t.length();i++){char ch =t.charAt(i);if(hash.containsKey(ch)){hash.put(ch,hash.get(ch)-1);if(hash.get(ch)<0){return ch;}}else{return ch;}}return ' ';}
}

如果你看到这段代码没有感觉到不对,那你一定没有仔细想过通用小技巧。

不难发现


if (hash.containsKey(ch)) {
hash.put(ch, hash.get(ch) + 1);
} else {
hash.put(ch, 1);
}

这部分代码可以变成


hash.put(ch, hash.getOrDefault(ch, 0) + 1);

中等题:难度 4

17.电话号码的字母组合

  • 这道题需要会回溯算法,哈希只起到一个存储字母映射的功能。不合适做教学
  • 那就这样结束了

为什么会有不同的哈希表?

想象一下,你有 100 个格子,但要存 1000 个东西。哈希函数把不同的 key 算出了相同的位置,这就是"哈希冲突"。怎么办?

不同的哈希表就是用不同的方法来解决这个问题的。

主要的哈希表种类

链表法哈希表(最常见)

  • 怎么做? 每个格子里放一个链表,冲突了就往链表后面加
  • 优点: 简单粗暴,永远不会"满"
  • 缺点: 链表太长查找就慢了
  • 谁在用? Java 的 HashMap、Python 的 dict

开放寻址法哈希表

  • 怎么做? 冲突了就继续往后找空位
  • 优点: 数据紧凑,缓存友好,速度快
  • 缺点: 容易"扎堆",删除操作麻烦
  • 谁在用? Python 早期版本、Redis 的字典

布谷鸟哈希(Cuckoo Hashing)

  • 怎么做? 用两个哈希函数,冲突了就把原来的元素"踢走"
  • 优点: 查找超快,最坏情况也是 O(1)
  • 缺点: 插入可能触发连锁反应
  • 适合: 读多写少的场景

一致性哈希(Consistent Hashing)

  • 怎么做? 把 key 和服务器都映射到一个环上

  • 优点: 增删节点时数据迁移量小

  • 适合: 分布式系统、负载均衡

  • 缺点:非常多,百度下吧

  • 就像选工具箱里的工具,锤子、螺丝刀、扳手各有各的用处。不同的哈希表就是为了应对不同的实际需求而生的,永远没有完美的解决方案,永远更优的解决方案。

    • 内存敏感?用开放寻址
    • 要求简单稳定?用链表法
    • 分布式场景?用一致性哈希
    • 读操作特别多?考虑布谷鸟哈希

题外话:哈希表的前世今生与永远的更优

前世

  • 1953 年,IBM 的 Hans Peter Luhn 首次提出了哈希的概念
  • 真正让哈希表发扬光大的是 1968 年的一篇论文,作者是 Robert Morris
  • 当时计算机内存很贵,程序员们迫切需要一种能快速查找数据、又不占太多空间的方法
  • 哈希表就是在这种背景下诞生的——用"空间换时间"的智慧

今生

  • 现在几乎所有编程语言都内置了哈希表:
    • Python 的 dict
    • Java 的 HashMap
    • JavaScript 的 ObjectMap
    • C++ 的 unordered_map
  • 数据库索引、缓存系统(Redis)、区块链、密码学都在用它
  • LeetCode 上大概有几百道题都能用哈希表

从 1953 年 IBM 的 Hans Peter Luhn 第一次提出哈希的概念,到今天各种花式哈希表遍地开花,这个数据结构已经走过了 70 多年。

每隔几年就会冒出新的变种——Robin Hood Hashing、Hopscotch Hashing、Swiss Table...工程师们像炼金术士一样,不断调整着时间、空间、复杂度之间的平衡。

也许这就是计算机科学的魅力:没有银弹,只有权衡。每一种"更优",都只是在特定场景下的"更合适"。

天才

  • 笔者在写这篇博客查阅资料时,偶然看到一个挺有意思的故事。罗格斯大学有个本科生,安德鲁·克拉皮文,有一天他读到一篇叫《Tiny Pointers》的论文,突然灵光一闪:诶,这个 tiny pointer 的内存占用还能再优化!
  • 于是他开始动手,打算用哈希表来存储 tiny pointer 指向的数据。结果在折腾的过程中,这哥们居然意外发现了一种速度更快的方法,这个方法,推翻了计算机科学界一个被奉为圭臬的理论——姚氏猜想。
  • 1985 年,图灵奖得主、著名计算机科学家姚期智在论文《Uniform Hashing Is Optimal》里说:对于某些特定类型的哈希表,最优的查找方法就是"均匀探测"(uniform probing),而且最坏情况下,插入一个元素的时间复杂度就是 O(x),没法再快了。而在今年,这条铁律被打破了。
  • 这相当于什么呢
    • 一个业余棋手在研究残局时,发现了一个被公认为"必输"的棋局其实有破解方法,推翻了世界冠军级大师的定论。
    • 一个土木工程本科生在做毕业设计时,找到了一种新的桥梁结构,突破了权威教授论文中"该跨度下的最大承重极限"。
    • 一个语言学学生在研究方言时,发现了一个反例推翻了著名语言学家提出的"某类语法结构在所有人类语言中都不存在"的结论
    • …………

姚期智:我用严密的数学证明这是极限。

克拉皮文:哦,我不知道,我就随便试试。

姚期智:???

  • 下图为:安德鲁·克拉皮文
  • 论文相关

image-20251006162659025

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/940884.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CF2128E2 Submedians (Hard Version)

考虑一个很直觉的东西,可行的中位数一定是连续的。具体来说,你将一个区间左右端点扩展,中位数一定只会有 \(+1, -1\) 的变化,因此覆盖到的区间一定是连续的。 我们找出可以的最小的中位数,和最大的中位数,类似莫…

Java 网络编程详解

Java 网络编程详解Java 网络编程是指通过 Java 语言实现计算机之间的网络通信,核心是利用 Java 提供的网络类库(如 java.net 包)操作 TCP/IP 协议,实现数据的发送与接收。无论是客户端与服务器的通信、分布式系统交…

Office365破解版下载(附永久激活密钥) 及安装使用教程

Office365破解版下载(附永久激活密钥) 及安装使用教程Office 365 是一套集成了 Word、Excel、PowerPoint、Teams、OneDrive 等工具的生产力套件,通过云服务实现高效协作与智能办公。以下是实用技巧,助你提升工作效率…

【URP】Unity中Mipmap Streaming原理与实现

摘要: Unity URP的纹理流送技术通过动态加载纹理的Mipmap层级优化显存使用。传统Mipmap会预加载所有层级(占用显存为原始纹理的4/3倍),而流送技术根据物体与摄像机的距离,仅加载当前所需的层级,其他层级按需异步…

如何设计PAD ring?

流程: 1)根据系统(其他芯片的)要求,芯片内部的floorplan,决定信号PAD的位置 2)计算出power PAD的个数,插入到信号PAD里面 3)加其他的PAD,比如IO filler,power cut,power on control,corner PAD,ESD等 细…

2025 年钢结构源头厂家最新推荐排行榜:聚焦美标欧标 / 环保设备 / 厂房别墅等多领域优质供应商,精选优质厂家助力企业精准选材

引言在当前钢结构行业快速发展的背景下,市场需求不断攀升,但行业乱象也随之凸显。部分企业资质不全却违规承接项目,导致工程质量与安全隐患;有些企业缺乏核心技术,产品性能不稳定,难以满足美标、欧标等高标准要求…

PostgreSQL 18 中国贡献者经验分享:开源参与的四点建议

2025 年 9 月 25 日,PostgreSQL 18 正式发布。该版本不仅修复了上百个问题,更带来了多项颠覆性的功能升级,例如全新的异步 I/O(AIO)框架、新增的跳跃式扫描(SKIP SCAN)技术,以及原生 UUIDv7 支持等,每一项升级…

C#实现连续语音转文字

一、基础实现方案(System.Speech) 1. 环境配置 // 安装NuGet包 Install-Package System.Speech// 添加语言包(控制面板) 控制面板 -> 语言 -> 添加中文语音包2. 核心代码实现 using System.Speech.Recogniti…

2025 年铝门窗厂家推荐排行榜,系统 / 智能 / 断桥 / 窄边 / 定制 / 全景 / 阳光房 / 隐框 / 隔声 / 防火铝门窗公司推荐

引言当前铝门窗市场需求攀升,但行业乱象凸显。部分小型厂家生产体系不完善,产品材质不达标、工艺粗糙,耐用性差;多数企业创新不足,产品同质化严重,难以满足消费者个性化需求;且不少品牌售后体系缺失,消费者维权…

如何把研究性学习糊弄过去

学校搞了个研究性学习,目测是计入综评分还是什么东西。课题只能从它给的内容里选,很地狱。 发现了一个识别手写数字的东西,这不是我们伟大的工程题吗!于是开始思考如何糊弄过去。 咕咕咕。

2025 年碳晶板厂家最新推荐榜:涵盖木纹 / 白色 / 全屋整装等品类,西南及全国优质品牌甄选指南

引言随着碳晶板在墙面装饰、全屋整装等领域应用愈发广泛,市场需求持续攀升,但行业乱象也随之凸显。部分品牌以次充好,用劣质基材降低成本,导致产品强度不足、耐候性差,甚至存在安全隐患;新品牌涌入后产品同质化严…

2025 年干细胞服务机构最新推荐排行榜:聚焦三体系认证与专利技术,精选优质机构供选择

引言当前干细胞技术在健康管理、疾病干预等领域应用愈发广泛,但行业乱象却让消费者难以抉择。部分机构缺乏规范制备流程,质量管理体系缺失,细胞产品安全与有效性无保障;一些机构研发能力薄弱,仅靠基础存储业务,无…

2025 最新隔音棉生产厂家口碑推荐榜:甄选家装公装专用材质,含西南 / 昆明阻尼片 / 吊顶 / 止震板品牌最新推荐

引言随着建筑声学需求升级,隔音棉已成为酒店、KTV、住宅等场景的刚需材料,但市场乱象让采购者举步维艰:部分产品隔音量虚标、防火等级不达标,潮湿环境易发霉老化;中小品牌交付延迟、售后缺位,公装项目常因材料问…

2025 灭老鼠公司最新推荐榜:欧盟认证技术加持,环保服务双优品牌权威甄选指南

引言后疫情时代,有害生物防制需求持续攀升,但鼠患治理市场乱象丛生:部分机构依赖低效化学药剂导致残留污染,通用化方案引发鼠患反复,应急响应滞后让家庭与企业蒙受额外损失,尤其餐饮、医疗等特殊场景更面临合规风…

2025 最新推荐!全国除甲醛公司权威榜单发布,解析蓉皓等标杆企业技术服务优势,覆盖新房 / 办公 / 学校多场景

引言据中国室内环境监测工作委员会数据,我国新装修住宅甲醛超标率居高不下,因治理不当引发的健康纠纷逐年上升。当前除甲醛市场品牌繁杂,部分企业缺乏甲级资质,使用的药剂未达环保标准,不仅除醛效果差,还易造成二…

KingbaseES V8R6清理冗余历史rman备份

KingbaseES V8R6清理冗余历史rman备份对于KingbaseES V8R6的通过sys_rman执行的物理历史备份,可以在执行备份时,备份的保留(retention)策略自动清理。不能通过手工删除备份,可以通过expire参数手工清理历史的冗余…

上周热点回顾(10.13

热点随笔:史诗级漏洞警报:ASP.NET Core 被曝 CVSS 9.9 分漏洞,几乎所有.NET 版本无一幸免! (马行空的博客) 每天10分钟,混剪视频Agent产出50条爆款,单月变现6位数(喂饭级教程) (AI架构师汤师爷) .NET 10 Rel…

一文读懂零知识证明Plonk 协议

下面是一篇专为“小白”准备的 Plonk 通俗科普,目标是让你 5 分钟内知道它到底在干什么、厉害在哪里,以及它背后的“魔法”到底是哪一招。一、先讲个故事:不泄密又能让人相信你 小明自称会瞬间移动,但不想把秘诀告…

P14259 兄妹(siblings)题解

闲话:这似乎是我第一次在 luogu 场切绿。蒟蒻对思维题不太擅长 QwQ。 前置芝士动态规划 / DP子集划分问题 / 可行性背包思路 首先观察这个放书的性质。结论:对于在同一个书架上的书,只需要一个人去负责。 证明也比较…