
第七课:《魔法通讯录------认识 unordered_set》
一、调查队的新任务
1、经过上一课的学习。
🕵️ 失踪宝石调查队已经学会了:
unordered_map
可以快速判断:
某个东西是否存在
2、有一天。
国王又发布了一项新任务:
"请记录所有进入城堡的人!"
名单如下:
Tom
Jack
Mike
3、国王继续问:
"Tom来过吗?"
"Jack来过吗?"
"Alice来过吗?"
智慧大臣突然发现:
这里根本不需要分数。
不需要年龄。
不需要电话。
4、只需要知道:
来过
或者
没来过
于是。
他拿出了一个新法宝:
🏆 unordered_set
二、为什么要发明 unordered_set?
1、先看看 unordered_map。
例如:
unordered_map<string,int> mp;
mp["Tom"] = 1;
mp["Jack"] = 1;
mp["Mike"] = 1;
这里:
Key → 姓名
Value → 1
2、问题来了。
这个:
= 1
真的有意义吗?
其实没有。
我们根本不关心:
1
是多少。
只关心:
Tom在不在
3、所以。
聪明的程序员说:
"既然Value没用。"
"那干脆不要Value了!"
4、于是:
unordered_set 诞生了
三、什么是 unordered_set?
1、可以把它理解成:
只有 Key 的哈希表
2、unordered_map:
Tom → 95
Jack → 88
Mike →100
3、unordered_set:
Tom
Jack
Mike
4、只保存数据本身。
没有:
95
88
100
这些Value。
四、创建通讯录
1、代码:
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<string> s;
}
2、这里:
unordered_set<string>
表示:
存储字符串集合
3、例如:
Tom
Jack
Mike
五、添加联系人
1、通讯录刚开始是空的。
添加:
s.insert("Tom");
通讯录:
Tom
2、继续:
s.insert("Jack");
s.insert("Mike");
变成:
Tom
Jack
Mike
六、insert是什么意思?
1、insert 翻译:
插入
2、例如:
s.insert("Tom");
意思:
把Tom放进集合
七、查询联系人
1、国王问:
"Tom来过吗?"
代码:
s.count("Tom")
2、结果:
1
表示:
存在
3、国王又问:
"Alice来过吗?"
代码:
s.count("Alice")
4、结果:
0
表示:
不存在
八、最常见写法
if(s.count("Tom"))
{
cout << "存在";
}
else
{
cout << "不存在";
}
输出:
存在
九、神奇的去重能力
1、通讯录管理员发现一个问题。
(1)Tom连续登记三次:
s.insert("Tom");
s.insert("Tom");
s.insert("Tom");
(2)通讯录会变成:
Tom
Tom
Tom
吗?
(3)答案:
❌ 不会
(4)结果仍然是:
Tom
2、因为:
set 天生不允许重复
这是它最强大的能力之一。
十、为什么叫集合?
1、数学课学过:
集合
(1)例如:
{1,2,3}
(2)里面:
1
只能出现一次。
(3)不会出现:
{1,1,1,2,3}
2、所以:
unordered_set
本质就是:
哈希集合
十一、自动去重演示
1、代码:
unordered_set<int> s;
s.insert(5);
s.insert(5);
s.insert(5);
s.insert(5);
2、最终集合:
5
只有一个。
十二、统计有多少种不同数字
1、这是竞赛中的经典题。
(1)输入:
1 5 2 1 3 5 5 2
(2)问题:
共有多少种不同数字?
(3)我们看:
1
2
3
5
(4)答案:
4种
2、程序怎么做?
(1)建立集合:
unordered_set<int> s;
(2)每读一个数字:
s.insert(x);
(3)过程:
1
1 5
1 5 2
1 5 2
1 5 2 3
1 5 2 3
1 5 2 3
1 5 2 3
(4)重复数字自动消失。
(5)最后:
cout << s.size();
(6)输出:
4
十三、size()是什么?
s.size()
1、表示:
集合中元素个数
2、例如:
unordered_set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
(1)此时:
cout << s.size();
(2)输出:
3
十四、完整实例
1、统计不同数字个数。
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
int n;
cin >> n;
unordered_set<int> s;
for(int i=0;i<n;i++)
{
int x;
cin >> x;
s.insert(x);
}
cout << s.size();
return 0;
}
2、输入:
8
1 5 2 1 3 5 5 2
3、输出:
4
十五、unordered_map 和 unordered_set 对比
1、unordered_map
保存:
Key → Value
例如:
Tom → 95
Jack → 88
创建:
unordered_map<string,int> mp;
适合:
统计次数
姓名查分数
学号查姓名
2、unordered_set
保存:
只有Key
例如:
Tom
Jack
创建:
unordered_set<string> s;
适合:
判断存在
查重
统计不同元素
十六、什么时候用哪个?
记住一句口诀:
1、如果需要:
姓名 → 分数
数字 → 次数
用:
unordered_map
2、如果只需要:
在不在?
用:
unordered_set
十七、比赛中的高频应用
1、以后看到:
有多少种不同数字
是否重复
是否访问过
单词是否出现过
用户名是否注册过
2、第一反应:
unordered_set
十八、小试牛刀
1、集合:
unordered_set<int> s;
s.insert(3);
s.insert(5);
s.insert(3);
s.insert(8);
2、问题1:
集合里有哪些数字?
答案:
3
5
8
3、问题2:
有几个数字?
答案:
3
4、问题3:
s.count(5)
结果是多少?
答案:
1
5、问题4:
s.count(10)
结果是多少?
答案:
0
本课总结
1、今天认识了哈希表家族的重要成员:
🏆 unordered_set
2、创建:
unordered_set<int> s;
3、插入:
s.insert(x);
4、判断存在:
s.count(x)
5、统计元素个数:
s.size()
6、最大特点:
自动去重
经典模板
1、判断是否存在:
if(s.count(x))
{
cout << "存在";
}
2、统计不同数字:
unordered_set<int> s;
for(int i=0;i<n;i++)
{
int x;
cin >> x;
s.insert(x);
}
cout << s.size();
魔法口诀
通讯录里记名字,
不用分数不用值。
insert负责来登记,
count负责来查询。
重复名字自动删,
去重能力最领先。
统计不同最拿手,
unordered_set真优秀!
下一课我们将进入哈希表经典的实战关卡:
《谁偷了国王的金币------查重问题》
你会学到很多竞赛高手最喜欢的技巧:
if(s.count(x))
一眼发现重复数字!