想象一下,你家有一大堆钥匙和信件,如果全堆在桌子上,每次找一把钥匙都得翻半天,这就是普通数组或列表的痛苦。 如果有个聪明的小伙伴帮你把每把钥匙分门别类,直接放到编号抽屉里,你想找哪把钥匙一眼就能找到,这就是 哈希表(Hash Table)的感觉。 今天我就带你体验这种“数据瞬间定位”的魔法工具——Java 的 HashMap 和 HashSet。
概念
哈希表就像一个智能抽屉系统:
- 每个抽屉都有编号,哈希函数 = 小伙伴帮你算出抽屉号,冲突怎么办? → 多个人的信件放到同一个抽屉里,用小盒子装起来
哈希表的本质就是:用聪明的计算公式,把你要找的东西直接放到能最快找到的地方
哈希函数:抽屉定位器
想象你有 100 个抽屉,要存钥匙,钥匙上有数字 ID。
哈希函数就像一个公式:
把钥匙ID % 100 → 得到抽屉编号
这样不管钥匙有多少,查找都是一步到位
再也不用像找零件一样一格格翻了
哈希冲突:抽屉里塞满了怎么办? 哈希函数可能把两把钥匙算到同一个抽屉,解决办法:
链地址法:在这个抽屉里放一个小盒子,里面放所有钥匙
开放地址法:找下一个空抽屉存
Java 的 HashMap/HashSet 就用链地址法 + 红黑树优化(当盒子太满时自动变成小树)
HashMap 就像你家抽屉里的钥匙:
key = 钥匙
value = 钥匙扣上的标签(比如钥匙用途)
map.put("Java书房钥匙", "书架A");
map.put("家门钥匙", "门锁");
map.put("车钥匙", "车库");
想找车钥匙?map.get("车钥匙"),一眼就找到抽屉里的钥匙扣,不用翻半天
HashSet:只管唯一性
- HashSet 就像你家抽屉只收独一无二的钥匙,重复的钥匙不收。实现上,HashSet 直接用 HashMap 存 key,value 用固定对象占位。
set.add("Java书房钥匙");
set.add("家门钥匙");
set.add("Java书房钥匙"); // 重复,没收
效果:
你看,“重复钥匙自动丢掉”,再也不用检查重复了
查找元素 → 哈希函数算出抽屉 → 一步拿到
删除元素 → 哈希函数算出抽屉 → 拿掉即可
这就是为什么 HashMap/HashSet 查找、插入、删除几乎都是 O(1)
你不再翻箱倒柜,直接开挂般找东西
抽屉 = 数组
哈希函数 = 算出抽屉编号的公式
链表/红黑树 = 抽屉里的小盒子
HashMap = 钥匙-钥匙扣对应关系
HashSet = 只收独一无二的钥匙
练习
1.
代码:
#include<iostream>
#include<string>
#include<unordered_set>
using namespace std;
int main()
{
int N;
cin>>N;
unordered_set<string>s;
for(int i=0;i<N;i++)
{
string x;
cin>>x;
s.insert(x);
}
cout<<s.size();
return 0;
}
解析:
unordered_set可以实现去重操作;
2.
代码:
#include<iostream>
#include<string>
#include<unordered_map>
using namespace std;
int main()
{
int n;
cin >> n;
unordered_map<string,int>students;
for (int i = 0; i < n; i++)
{
string name;
cin >> name;
students[name] = 0;
}
int m; cin >> m;
for (int i = 0; i < m; i++)
{
string s;
cin >> s;
auto it = students.find(s);
if (it == students.end())
{
cout << "WRONG" << endl;
}
else if (it->second == 0)
{
cout << "OK" << endl;
it->second = 1;
}
else if (it->second == 1)
{
cout << "REPEAT" << endl;
}
}
return 0;
}
解析:
这道题主要就是利用哈希表,哈希表时间复杂度小,O(1),非常快速,本题思路是首先利用迭代器it和find函数判断能不能找到所输入字符串,找到了的话就将it->second改为1(最开始初始化是0),这样下次找到的话,值变为1,就返回REAPEAT;