STL之——set
\(\texttt{set}\) 是什么
\(\texttt{set}\) 是 \(\texttt{C++ STL}\) 中提供的容器,\(\texttt{set}\) 也是数学上的集合——具有唯一性,即每个元素只出现一次,而 \(\texttt{multiset}\) 则是可重集,两者的内部实现是一棵红黑树,它们支持的函数基本相同。\(\texttt{set}\) 用于判断一个集合内元素的有无。
头文件及声明
#include <set>
set<int> s1;
set<pair<int, int> > s2;
set<set<int> > s3; //可以嵌套
set<vector<int> > s4; //可自选类型
就像其他需要排序的数据类型一样,为一个结构体的 \(\texttt{set}\),需要重载小于号。
struct node{
......;
bool operator <(const node &other) {
return x > other.x;
}
};
set<node> s;
内置函数
\(\texttt{set.size()}\)
统计 \(\texttt{set}\) 中元素个数,函数返回一个整形变量,表示 \(\texttt{set}\) 中元素个数,时间复杂度 \(O(1)\)。
用法:名称.size();
eg.
int num = s.size();
\(\texttt{set.empty()}\)
检查 \(\texttt{set}\) 是否为空,返回一个 \(\texttt{bool}\) 型变量,\(1\) 表示 \(\texttt{set}\) 为空,否则为非空,时间复杂度 \(O(1)\)。
用法:名称.empty();
eg.
if(s.empty()) cout << "Myset is Empty." << endl;
\(\texttt{set.clear()}\)
清空 \(\texttt{set}\),无返回值。
用法:名称.clear();
eg.
s.clear();
\(\texttt{set.count(x)}\)
返回 \(\texttt{set}\) 或 \(\texttt{multiset}\) 中值为 x 的元素个数,时间复杂度为 \(O(\log n)\)。
用法:名称.count(x)
eg.
if(!s.count(x)) ans++;
迭代器
双向访问迭代器,不支持随机访问,支持星号解除引用,仅支持 ++--
这两个算术操作。
引用和操作:
set<类型>::iterator it;
eg.
set<int>::iterator it = s.begin();
it++;
it--;
若把 it++
,则 \(it\) 将会指向“下一个”元素。这里的下一个是指在 \(key\) 从小到大排序的结果中,排在 \(it\) 下一名的元素。同理,若把 it--
,则 \(it\) 会指向排在上一个的元素。
++ --
操作的复杂度均为 \(O(\log n)\)
遍历 \(\texttt{set}\) 及访问其中的元素
//set
for(set<int>::iterator it = s.begin(); it != s.end(); it++)
cout << *it << endl; //取出这个迭代器指向的元素
//set嵌套
for(set<set<int> >::iterator it = s.begin(); it != s.end(); it++)
{
//首先取出set中嵌套的set
for(set<int>::iterator rit = (*it).begin(); rit != (*it).end(); rit++)
cout << *rit << ' '; //遍历这个set
cout << endl;
}
\(\texttt{set.begin()}\)
返回集合的首迭代器,即指向集合中最小元素的迭代器,时间复杂度为 \(O(1)\)。
用法:名称.begin();
eg.
map<int>::iterator it = s.begin();
\(\texttt{set.end()}\)
返回集合的尾迭代器,众所周知,\(\texttt{STL}\) 中区间都是左闭右开的,那么 \(\texttt{end()}\) 函数返回的迭代器即为指向集合中最大元素的下一个位置的迭代器,因此 \(\texttt{s.end()}\) 才是指向集合中最大元素的迭代器,时间复杂度为 \(O(1)\)。
用法:名称.end();
eg.
maxn = *(--s.end()); //取出最大元素
\(\texttt{set.insert(x)}\)
在 \(\texttt{set}\) 中插入元素,返回插入地址的迭代器和是否插入成功的 \(\texttt{bool}\) 并成的 \(\texttt{pair}\) ,时间复杂度为 \(O(\log n)\)。
PS:\(\texttt{set}\) 在进行插入的时候是不允许有重复的键值的,如果新插入的键值与原有的键值重复则插入无效(\(\texttt{multiset}\) 可以重复)。
用法:名称.insert(set类型);
eg.
s.insert(3);
\(\texttt{set.erase(参数)}\)
删除,参数可以是元素或者迭代器,返回下一个元素的迭代器,时间复杂度为 \(O(\log n)\)。
,注意在 \(\texttt{multiset}\) 中 \(\texttt{s.erase(x)}\) 会删除所有值为 \(x\) 的元素。
\(\texttt{erase}\) 函数还可以删除某个范围内的元素,这个函数接受两个迭代器 \(s\) 和 \(t\),把 \([s,t)\) 范围内的东西都删掉。
用法:名称.erase(参数);
eg.
set<int>::iterator it = s.begin();
s.erase(it);
s.erase(3);
s.erase(itl, itr);
\(\texttt{set.find(x)}\)
在 \(\texttt{set}\) 中查找值为 \(x\) 的元素,并返回指向该元素的迭代器,若不存在,返回 \(\texttt{set.end()}\),时间复杂度为 \(O(\log n)\)。
用法:名称.find(x);
eg.
if(s.find(x) != s.end()) cout << "Have Found!" << endl;
\(\texttt{set.lower\_bound(x) / upper\_bound(x)}\)
两个神奇的东西,决定把他们放在一块谈一谈。
用法与 \(\texttt{find}\) 类似,但查找的条件略有不同,时间复杂度 \(O(\log n)\)。
\(\texttt{s.lower\_bound(8)}\) 表示查找 \(\ge x\) 的元素中最小的一个,并返回指向该元素的迭代器。
\(\texttt{s.upper\_bound(8)}\) 表示查找 \(> x\) 的元素中最小的一个,并返回指向该元素的迭代器。
举个例子:
在 \(\texttt{set}\{3,5,7,8,13,16\}\)中
对于在 \(\texttt{set}\) 中存在的元素,比如 \(8\)。
\(\texttt{s.lower\_bound(8)}\) 返回 \(8\) 所在位置的迭代器。
\(\texttt{s.upper\_bound(8)}\) 返回 \(13\) 所在位置的迭代器。
对于在 \(\texttt{set}\) 中不存在的元素,比如 \(12\)。
两个函数返回的则都是 \(13\) 所在位置的迭代器。
特殊地,对于比 \(\texttt{set}\) 中最大的元素大的元素,比如 \(20\)
两个函数返回的都是 s.end()。