set表示一个排好序的集合,其包含的元素是唯一的,set中的元素会自动排序,不能直接修改它的元素值。
set的底层数据结构是红黑树的变体,插入和删除操作比vector快。
multiset和set差不多,但set每个元素值只能出现一次,而multiset中同一值可以出现多次。
unordered_set表示一个不排序的结合,其包含的元素时唯一的,unordered_set中的元素不会自动排序,也不能直接修改它的元素值。
unordered_set的底层数据结构是hash表,插入和查找操作比set快。需要用到hash表的地方可以用unordered_set来实现。
PS:在非STL库中还有hash_set容器,但由于STL后来加入了unordered_set,hash_set现在基本不用了,毕竟官方的更加权威。
unordered_multiset和unordered_set差不多,区别仍然体现在每个元素值能出现一次还是多次。
1、构造和拷贝构造(这些容器一般不赋初值)
#include<iostream>
#include<set> //包含set和multiset
#include<unordered_set> //包含unordered_set和unordered_multiset
using namespace std;
//普通构造
set<int> s1;
multiset<int> ms1;
unordered_set<int> hash_s1;
unordered_multiset<int> hash_ms1;
//拷贝构造
set<int> s2(s1);
multiset<int> ms2(ms1);
unordered_set<int> hash_s2(hash_s1);
unordered_multiset<int> hash_ms2(hash_ms1);
auto cmp1 = [](char a, char b) {return (a > b); }; //cmp1为比较器。若返回值为true,则a排b前面,反之排后面。
//也可以写成下面的形式,不过调用的时候有不一样。
auto cmp2(char a, char b)
{
return (a > b);
}
//以下各种构造只拿set举例。
set<int> s3(s1.begin(),s1.end()); //两个参数,分别为迭代器。用s1的部分值生成。
set<int, decltype(cmp1)> s4(cmp1); //用来产生一个以cmp1为排序准则的set。
set<int, decltype(cmp1)> s5(s1.begin(),s1.end(),cmp1); //三个参数,将s1的部分值按cmp1为排序准则生成s5。
//调用cmp2的方式
set<int, decltype(&cmp2)> m5(m1.begin(),m1.end(),&cmp2);
2、大小和是否为空
//仅拿set举例
s1.size() //返回s1的大小
s1.empty() //若s1为空则返回true,否则返回false。
3、插入、删除、查找和计数
#include<vector>
//插入,仅以set举例
/*
返回pair<iterator,bool>,前一个为迭代器——指向新插入的元素或原有的该元素,后一个为bool——表示是否插入成功。
分别用.first和.second来访问。
*/
s1.insert(2); //将2插入s1中。s1中值为{2}。
s1.emplace(2); //和前面一样的效果,这时2已经存在,不能再插入,返回值.second等于false。
//无返回值。
vector<int> v={1,4,6};
s1.insert(v.begin(), v.end()); //将区间内的数全都插入s1中。s1中值为{1,2,4,6}。
//下面这个一般也用不到
//返回一个迭代器——指向新插入的元素或原有的该元素。
s1.insert(s1.begin(),5); //将5插入s1中,第一个参数为迭代器作为搜索起点,提升插入速度。s1中值为{1,2,4,5,6}。
/*
PS:很奇怪的是s1.insert(s1.begin()+3,5);这样写是错误的,提示“没有与这些操作数匹配的‘+’操作符”。
可能和底层结构为树有关。经试验,unordered_set也提示这样的错,估计底层结构为hash表也不能通过加法找到其他的迭代器。
更奇怪的是auto it = s1.begin(); it++;这样写居然是可以的,没有加法,但能自增。
*/
//删除
s1.erase(5); //删除5。s1中值为{1,2,4,6}。在multiset中这种删除方法会删除所有这个值的元素,而不是删除一个,返回值为删除的个数。
s1.erase(s1.begin()); //删除迭代器指向的元素。s1中值为{2,4,6}。
s1.erase(s1.begin(),s1.end()); //删除区间中元素,可以配合find使用。s1中值为{}。返回删除的元素个数。
s1.clear(); //移除全部元素,清空容器。
//multiset中将元素5的个数减少1
ms1.erase(ms1.find(5));
//查找。设此时s1值为{1,2,4,5,6}
s1.find(4); //查找元素4。若找到则返回该元素对应迭代器,找不到则返回s1.end()。
//计数。
s1.count(4); //查找元素4的个数。对于set只会返回0或1,对于multiset则返回元素的个数,可以为多个。
4、set的遍历
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
for (int i = 0; i < 10; i++)
s.insert(i*i);
set<int>::iterator iter;
iter = s.begin();
while(iter != s.end()){
cout << *iter << endl;
iter++;
}
for (iter = s.begin();iter != s.end(); iter++){
cout << *iter << endl;
}
for(auto &data : s){
cout << data <<endl;
}
return 0;
}
5、关于元素类型为结构体或类的unordered_set和unordered_multiset的使用
当元素类型为结构体或类时,由于使用了hash表的结构,必须要为该结构体或类重载“==”符号和写上对应hash函数的结构体。
具体方法见https://blog.csdn.net/zhangxiao93/article/details/73741460