学会吊打面试官之set
小白:大牛,我最近在学习C++的STL容器,发现set好像是一种比较特殊的容器,可以帮我简单介绍一下吗?
大牛:没问题,set是一种关联式容器,它的主要特点是不允许有重复元素,并且元素是有序的。
小白:不允许有重复元素?那和数组或者向量有什么区别呢?
大牛:数组和向量都是顺序容器,允许有重复元素,而set则是关联式容器,它的内部实现是基于红黑树的,因此在插入、删除元素时都有比较高的效率。
小白:那我该如何使用set呢?
大牛:set有两个常用的操作:插入元素和查找元素。你可以使用insert函数插入元素,使用find函数查找元素。
下面给你一个例子,我们定义一个set来存储一组学生的分数,然后插入几个分数,最后查找一个分数是否在set中。
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> scores; // 定义一个set来存储分数
// 插入几个分数
scores.insert(85);
scores.insert(72);
scores.insert(93);
scores.insert(68);
scores.insert(78);
// 查找一个分数是否在set中
if (scores.find(85) != scores.end()) {
cout << "85分在set中" << endl;
} else {
cout << "85分不在set中" << endl;
}
return 0;
}
小白:我看到你在代码里用了scores.end()
,这是什么意思呢?
大牛:end()
函数返回的是set中最后一个元素之后的位置,它通常用于表示一个不存在的元素。在上面的例子中,如果85分不存在于set中,那么scores.find(85)
将返回scores.end()
,从而判断85分不在set中。
小白:原来如此,set真的很方便啊,不过我还有一个问题,set的底层实现是什么?
大牛:set的底层实现是基于红黑树,它是一种自平衡的二叉查找树,可以在O(log n)的时间内完成插入、删除、查找等操作。你可以自己实现一下红黑树来加深理解。
小白:好的,我会去看看红黑树的实现,谢谢你的讲解。
大牛:不客气,有问题可以随时问我。
小白:那set和map有什么区别呢?
大牛:set和map都是关联容器,但是set中存储的是一组不重复的元素,而map则是键值对的集合,每个键值对都是独一无二的。
小白:明白了!那我们来看看set的使用场景吧。
大牛:set通常用于需要快速查找、插入和删除元素的情况。比如,你想要对一个列表进行去重操作,就可以使用set来实现。
小白:噢,那我可以用set来实现这个功能了。请问,set的实现原理是什么呢?
大牛:set的底层实现通常是基于平衡二叉树(比如红黑树)或者哈希表。对于平衡二叉树实现的set,查找、插入和删除的时间复杂度都是O(log n),而哈希表实现的set,这些操作的时间复杂度通常是O(1)。
小白:这样啊,看来使用set还是挺方便的。你能给我举个例子吗?
大牛:当然可以。假设你有一个字符串列表,需要对其中的重复字符串进行去重操作,代码如下:
#include <iostream>
#include <set>
#include <string>
int main() {
std::set<std::string> mySet;
std::string str;
while (std::cin >> str) {
mySet.insert(str);
}
for (const auto& s : mySet) {
std::cout << s << " ";
}
std::cout << std::endl;
return 0;
}
这个程序会不断从标准输入中读取字符串,然后将其插入到set中。由于set会自动去重,所以最终只会输出不重复的字符串。
小白:太厉害了!看来set真的很实用。
大牛:是的,set和其他STL容器一样,提供了丰富的操作和算法,可以让我们更加方便地处理数据。
大牛:没问题,这个例子我来给你举一个更加具体的应用场景。假设我们有一个学生名单,里面存储着所有选修了某一门课程的学生的学号,现在我们要求出选修这门课的学生数量。
小白:好的,那我们该怎么做呢?
大牛:我们可以使用set来存储所有选修这门课的学生学号,因为set具有不重复的特性,所以存储学号的时候不会出现重复的情况。
小白:明白了,那我们该怎么使用set呢?
大牛:首先,我们需要定义一个set对象,可以这样写:
std::set<int> studentSet;
这个定义语句表示我们定义了一个存储int类型数据的set对象,名字叫做studentSet。
接下来,我们需要将所有选修了这门课程的学生的学号加入到这个set对象中,可以使用insert函数来实现:
studentSet.insert(10001);
studentSet.insert(10002);
studentSet.insert(10003);
这些代码表示我们将学号为10001、10002和10003的学生加入到了set对象中。
最后,我们只需要使用size函数来获取set对象中元素的数量就可以了:
std::cout << "选修这门课的学生数量为:" << studentSet.size() << std::endl;
小白:哇,这个例子非常好理解,我觉得我已经掌握了set的使用方法了。但是,我还有一个问题,set是如何实现的呢?
大牛:set是使用红黑树来实现的,这也是set具有自动排序和去重功能的原因。红黑树是一种自平衡的二叉搜索树,具有良好的插入、删除和查找性能。
小白:原来如此,谢谢大牛的解答,我学到了很多新的知识!
大牛:不客气,希望你能在以后的工作中有所收获!
大牛:set底层使用红黑树实现,这是一种自平衡二叉查找树,可以保证插入、删除、查找等操作的时间复杂度都是O(log n)。所以,当需要高效率的查找、插入、删除时,set是一个不错的选择。
小白:那我平常怎么会用到set呢?
大牛:set的应用场景很多,比如去重、排序、查找等。举个例子,假设你要对一篇文章中的单词进行去重操作,那么可以使用set来存储这些单词,因为set会自动去重,而且可以在插入的同时进行排序。
小白:哦,我懂了。你能再举个例子吗?
大牛:当然可以。比如说你要在一组数据中查找出不重复的元素,可以使用set来实现。具体代码如下:
#include <iostream>
#include <set>
using namespace std;
int main() {
int arr[] = {1, 2, 3, 4, 3, 2, 5, 6, 4, 7};
int n = sizeof(arr) / sizeof(arr[0]);
set<int> s;
for (int i = 0; i < n; i++) {
s.insert(arr[i]);
}
for (auto x : s) {
cout << x << " ";
}
return 0;
}
这段代码中,我们使用set来存储arr数组中的元素,并且自动去重和排序。最后,我们遍历set中的元素,输出不重复的结果。
小白:哇,这个例子好棒啊!我明白了,set真的很方便呢。