了解set
1. set的介绍
C++中的set是一种关联式容器,它能够存储同一数据类型的数据,并且能从一个数据集合中取出数据。在set中,每个元素的值都唯一,而且系统能根据元素的值自动进行排序 。set是用红黑树实现的,集合中的每个元素只出现一次,并且是排好序的(默认按键值升序排列),但可以通过自定义比较函数来改变排序方式 。
红黑树是一种自平衡的二叉搜索树,它可以保证插入、删除、查找等操作的时间复杂度为O(log n)。
set常用操作有:插入、删除、查找、遍历等。在C++ STL中,set容器定义在头文件中 。
2. set的使用
1. Member functions
constructor(构造):
函数声明 | 功能介绍 |
---|---|
set (const Compare& comp = Compare(), const Allocator& = Allocator() ); | 构造空的set |
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); | 用[first, last)区间中的元素构造set |
set ( const set<Key,Compare,Allocator>& x); | set的拷贝构造 |
destructor(析构):
~set():这将销毁所有容器元素,并释放设置容器使用其分配器
operator= copy (1) set& operator= (const set& x);
2.set的迭代器
迭代器是一种类似于指针的对象,它可以表示容器中元素的位置,并用于遍历容器中的元素,迭代器通常通过容器类提供的成员函数来获取,例如begin(), end(), rbegin(), rend()等。
函数声明 | 功能介绍 |
---|---|
begin | 返回set中起始位置元素的迭代器 |
end() | 返回set中最后一个元素后面的迭代器 |
rbegin() | 返回set第一个元素的反向迭代器,即end |
rend() | 返回set最后一个元素下一个位置的反向迭代器,即rbegin |
cbegin() | 返回set中起始位置元素的const迭代器 |
cend() | 返回set中最后一个元素后面的const迭代器 |
crbegin() | 返回set第一个元素的反向const迭代器,即cend |
crend() | 返回set最后一个元素下一个位置的反向const迭代器,即crbegin |
#include <iostream>
#include <set>
using namespace std;
int main()
{
int a[] = { 3,5,2,4,1 };
set<int> s1;//构造空的set
set<int> s2(a, a + sizeof(a) / sizeof(a[0]));//用[first, last)区间中的元素构造set
set<int> s3(s2);//set的拷贝构造
//迭代器
set<int>::iterator it1 = s2.begin();
cout << "s2 :";
while (it1 != s2.end())
{
cout << *it1 << ' ';
++it1;
}
cout << endl;
set<int>::reverse_iterator it2 = s3.rbegin();
cout << "s3 :";
while (it2 != s3.rend())
{
cout << *it2 << ' ';
++it2;
}
cout << endl;
return 0;
}
运行结果如下:
3. set的容量
函数声明 | 功能介绍 |
---|---|
empty | 检测set是否为空,空返回true,否则返回false |
size | 返回set中有效元素的个数 |
max_size | 返回set容器可以容纳的元素的最大数量 |
int main()
{
set<int> s = { 3,5,4,2,1 };
cout << "s.empty:" << s.empty() << endl;
cout << "s.size:" << s.size() << endl;
cout << "s.max_size:" << s.max_size() << endl;
return 0;
}
4.set修改操作
4.1 insert
在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>。pair包含两个成员变量,分别命名为first和second,它们分别存储了第一个和第二个元素的值。
int main()
{
set<int> s = { 3,5,4,2,1 };
auto p1 = s.insert(6);//插入成功
auto p2 = s.insert(2);///插入失败
cout << (*p1.first) << ":" << p1.second << endl;
cout << (*p2.first) << ":" << p2.second << endl;
return 0;
}
运行结果如下:
4.2 erase、swap和clear
函数声明 | 功能介绍 |
---|---|
erase | 删除set中的元素 |
swap | 交换set中的元素 |
clear | 将set中的元素清空 |
erase:
void erase ( iterator position ):删除set中position位置上的元素
size_type erase ( const key_type& x ):删除set中值为x的元素,返回删除的元素的个数
void erase ( iterator first, iterator last ):删除set中[first, last)区间中的元素
int main()
{
set<int> s = { 3,5,4,2,1 };
set<int>::iterator it = s.begin();
++it; ++it; //it指向3的位置
s.erase(it); //删除set中position位置上的元素
it = s.begin();
while (it != s.end())
{
cout << *it << ' ';
++it;
}
cout << endl;
s.insert(3);
size_t e = s.erase(3); //删除set中值为x的元素,返回删除的元素的个数
cout << "删除的个数:" << e << endl;
s.insert(3);
set<int>::iterator first = s.begin(); ++first;
set<int>::iterator last = s.end(); --last;
s.erase(first, last); //删除set中[first, last)区间中的[2,5)
it = s.begin();
while (it != s.end())
{
cout << *it << ' ';
++it;
}
cout << endl;
return 0;
}
运行结果如下:
swap和clear:
int main()
{
set<int> s1 = { 3,5,4,2,1 };
set<int> s2;
s2.swap(s1);
return 0;
}
调试结果如下:
4.3 emplace
std::set::emplace()函数是C++11中引入的一个新函数,它可以在不构造临时对象的情况下直接在set中构造元素。这个函数的用法和std::set::insert()函数类似,但是它可以避免不必要的构造和析构操作,从而提高程序的效率。具体来说,std::set::emplace()函数会在set中插入一个新元素,并返回一个指向这个元素的迭代器。如果这个元素已经存在于set中,则不会插入新元素,而是返回一个指向已有元素的迭代器。
struct Person
{
string name;
int age;
Person(const string& n, int a)
: name(n)
, age(a)
{}
bool operator<(const Person& other) const
{
return name < other.name;
}
};
int main()
{
set<Person> people;
people.emplace("Alice", 25);
people.emplace("Bob", 30);
people.emplace("Charlie", 20);
for (const auto& person : people)
{
cout << person.name << " is " << person.age << " years old.\n";
}
return 0;
}
上面的代码定义了一个名为Person的结构体,它包含一个字符串类型的name成员和一个整数类型的age成员。然后,我们定义了一个std::set类型的people变量,并使用std::set::emplace()函数向其中插入了三个Person对象。最后,我们遍历了people变量,并输出了每个Person对象的name和age成员。
5. observers(了解)
在C++中,std::set是一个关联容器,它包含一组经过排序的Key类型的唯一对象。key_comp函数返回一个比较对象,该对象可用于比较两个元素,以确定在容器1定义的严格弱排序中,第一个元素是否在第二个元素之前
int main()
{
set<int> my_set = { 1, 2, 3, 4, 5 };
auto comp = my_set.key_comp();
for (auto it = my_set.begin(); it != my_set.end(); ++it)
{
cout << *it << " ";
if (comp(*it, 3))
{
cout << "goes before 3\n";
}
else if (comp(3, *it))
{
cout << "goes after 3\n";
}
else
{
cout << "is equal to 3\n";
}
}
return 0;
}
运行结果如下:
value_comp函数是std::set类的公共成员函数,它返回一个比较对象,该对象可用于比较两个元素,以确定它们在集合中的相对顺序。value_comp返回的比较对象是由std::set类的Compare模板参数构造的。默认情况下,此参数设置为std::less<Key>,这意味着元素按升序排列。如果要按降序排列元素,可以在定义集合时传递std::greater<Key>作为第二个模板参数。
以下是如何使用value_comp比较集合中两个元素的示例:
int main()
{
set<int> mySet = { 1, 2, 3, 4, 5 };
auto comp = mySet.value_comp();
cout << "mySet contains:";
for (auto it = mySet.begin(); it != mySet.end(); ++it)
{
cout << ' ' << *it;
}
cout << '\n';
int highest = *mySet.rbegin();
set<int>::iterator it = mySet.begin();
do
{
cout << *it << " ";
} while (comp(*(++it), highest));
cout << '\n';
return 0;
}
运行结果如下:
在本例中,我们首先创建一个名为mySet的集合,该集合包含从1到5的整数。然后,我们使用value_comp()函数创建一个名为comp的比较对象。我们使用这个比较对象来迭代集合中的元素,并按升序打印出来。
6.Operations(了解)
set中的find函数用于查找给定元素是否存在于set中。如果存在,则返回指向该元素的迭代器;否则,返回set::end() 。
set中的count函数用于返回set中等于给定值的元素的数量。 如果set中不存在该元素,则返回0 。
set中的lower_bound和upper_bound函数都是用于查找元素的函数。lower_bound函数返回一个迭代器,该迭代器指向第一个大于或等于给定值的元素。upper_bound函数返回一个迭代器,该迭代器指向第一个大于给定值的元素。 这两个函数都需要一个参数,即要查找的值。如果找到了该值,则返回指向该值的迭代器。如果没有找到该值,则返回指向下一个较大元素的迭代器。如果没有下一个较大元素,则返回set::end() 。
equal_range()函数返回一个迭代器对,表示与给定值相等的元素范围。第一个迭代器指向第一个等于给定值的元素,第二个迭代器指向最后一个等于给定值的元素的下一个位置。如果没有等于给定值的元素,则两个迭代器均指向第一个大于该值的元素。这个函数的时间复杂度为O(log(n))。
int main()
{
//find
cout << "find" << endl;
set<int> s = { 3,5,1,4,2 };
cout << *s.find(3) << endl; //返回指向该元素的迭代器
set<int>::iterator it = s.find(6); //返回set::end()
cout << (it == s.end()) << endl;
cout << endl;
//count
cout << "count" << endl;
if (s.count(3))
{
cout << "3 is present in the set" << endl;
}
else
{
cout << "3 is not present in the set" << endl;
}
if (s.count(6))
{
cout << "6 is present in the set" << endl;
}
else
{
cout << "6 is not present in the set" << endl;
}
cout << endl;
//lower_bound upperr_bound
cout << "lower_bound upperr_bound" << endl;
set<int>::iterator l = s.lower_bound(2); //返回一个迭代器,该迭代器指向第一个大于或等于给定值的元素
cout << *l << endl;
l = s.upper_bound(2); //返回一个迭代器,该迭代器指向第一个大于给定值的元素
cout << *l << endl;
cout << endl;
//equal_range
cout << "equal_range" << endl;
auto range = s.equal_range(3); //返回一个迭代器对,表示与给定值相等的元素范围
for (auto it2 = range.first; it2 != range.second; ++it2)
{
cout << *it2 << " ";
}
cout << endl;
return 0;
}
运行结果如下:
3.set有关的题目
首先可以直接用一个数组去遍历另一个数组,“完美” N^2的时间复杂度,这种方法直接舍去。
将数组中的元素输入到set中,可以得到有序而且不重复的数据,然后在找两个数列中的交集就非常简洁。(set的作用就是排序加去重)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
// 先去重
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
// set排过序,依次比较,小的一定不是交集,相等的是交集
set<int>::iterator it1 = s1.begin();
set<int>::iterator it2 = s2.begin();
vector<int> ret;
while(it1 != s1.end() && it2 != s2.end())
{
//小的++
if(*it1 < *it2)
{
it1++;
}
else if(*it2 < *it1)
{
it2++;
}
else
{
ret.push_back(*it1);
it1++;
it2++;
}
}
return ret;
}
};