博主粉丝群介绍: ① 群内初中生、高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。
② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。
③ 群内也有职场精英,大厂大佬,可交流技术、面试、找工作的经验。
进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬。
进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。
群公告里还有全网大赛and约稿汇总/博客提效工具集/CSDN自动化运营脚本 有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。
知识点 | 重要程度 | 说明 |
---|---|---|
数组基础 | 必需⭐ | 需要了解数组的基本操作和时间复杂度 |
时间复杂度 | 必需⭐ | 需要理解 O(1)、O(n) 等概念 |
基础编程语法 | 推荐 | 会使用一门编程语言即可 |
链表 | 加分项 | 了解更佳,不了解也不影响 |
Hash 的英文原意就是"切碎、混杂"。想象你在炖一锅大杂烩(hash):胡萝卜、土豆、红肉、白肉……很多复杂叫不出名的食材混合在一起煮,最后成了一锅汤。
这个过程就是哈希。
原本各有特点的食材(数据),经过烹煮(哈希函数),变成了一锅融合的汤(Hash值)。
哈希的本质:把不能当索引的东西(字符串、对象)变成能当索引的数字,但对外呈现为键值对,更符合人类思维。
通过序号(索引),像门牌号一样:
数组:[苹果, 芒果, 葡萄, 草莓, 西瓜]
索引: 0 1 2 3 4
问题来了:我想找到"葡萄"要怎么找?
方法1:暴力查找(遍历挨个比对)
第1步:看索引0 → "苹果" 不是
第2步:看索引1 → "芒果" 不是
第3步:看索引2 → "葡萄" 找到了!
时间复杂度:O(n) —— 最坏情况要找 n 次
直接访问:数组[2] → "葡萄" 一次搞定!
时间复杂度:O(1) —— 直达,不用挨个找
哈希表就是来解决这个问题的!
用哈希函数把"葡萄"变成索引
第一步:设计一个哈希函数(就像炖汤的配方)
hash("葡萄") = 2 ← 把"葡萄"这个字符串"煮"成数字2
第二步:存储时
"葡萄" → hash("葡萄") = 2 → 存到数组[2]的位置
第三步:查找时
想找"葡萄"?
→ hash("葡萄") = 2
→ 直接去数组[2]拿
→ 时间复杂度:O(1)!
查找方式 | 普通数组遍历 | 哈希表 |
---|---|---|
操作 | 从头到尾挨个比对 | 通过哈希函数直接定位 |
过程 | 苹果 → 芒果 → 葡萄 | hash("葡萄")=2 → 数组2 |
时间复杂度 | O(n) | O(1) |
特性 | 普通数组 | 哈希表(基于数组) |
---|---|---|
底层结构 | 数组 | 数组 |
索引是什么 | 必须是连续的数字 0,1,2,3... | 任意类型(通过哈希函数转成数字) |
存储方式 |
|
|
查找速度 | O(1)(知道索引)不知道O(n) | O(1) 平均 / O(n) 最坏 |
// 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 → 芒果
还是用数组!
用户看到的(键值对形式):
"apple" → "苹果"
"grape" → "葡萄"
底层实际存储(数组):
[ null, null, "葡萄", "苹果", null ]
0 1 2 3 4
↑ ↑
hash("grape") hash("apple")
= 2 = 3
过程:
"apple" → "苹果"
hash("apple") = 3
[3]
位置hash("apple") = 3
,直接去 [3]
拿方法 | 描述 | 示例代码 |
---|---|---|
| 向 |
|
| 根据给定的键返回对应的值,如果键不存在,则返回 |
|
| 检查 |
|
| 检查 |
|
| 删除指定键的键值对,返回该键的值,如果键不存在则返回 |
|
| 清空 |
|
| 返回 |
|
| 判断 |
|
| 返回 |
|
| 返回 |
|
| 返回 |
|
getOrDefault | 根据键 | map.getOrDefault("key1", "defaultValue"); |
给你一个整数数组 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
``` java
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; // 返回符合条件的配对数
}
}
不知道你有没有一个感觉,觉得我文中的代码是可以化简的?
如果你没有这种感觉,那我建议回过头去看 哈希表中常用的方法有哪几个? 这一小结内容
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);
给定两个字符串 s 和 t ,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
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);
想象一下,你有100个格子,但要存1000个东西。哈希函数把不同的key算出了相同的位置,这就是"哈希冲突"。怎么办?
不同的哈希表就是用不同的方法来解决这个问题的。
dict
HashMap
Object
和Map
unordered_map
从1953年IBM的Hans Peter Luhn第一次提出哈希的概念,到今天各种花式哈希表遍地开花,这个数据结构已经走过了70多年。
每隔几年就会冒出新的变种——Robin Hood Hashing、Hopscotch Hashing、Swiss Table...工程师们像炼金术士一样,不断调整着时间、空间、复杂度之间的平衡。
也许这就是计算机科学的魅力:没有银弹,只有权衡。每一种"更优",都只是在特定场景下的"更合适"。
姚期智:我用严密的数学证明这是极限。
克拉皮文:哦,我不知道,我就随便试试。
姚期智:???
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。