set容器在插入数据的时候会自动对其排序,比如插入的数据是“1 3 4 2 5”,那么到输出的时候可能就变成了“1 2 3 4 5”。 set/multuset属于关联式容器,它们的底层结构是二叉树。set和multiset的区别在于,set不允许容器内部有重复元素,而multiset允许有重复元素。不管是set还是multiset,在使用的时候,只需要包含一个头文件<set>。
构造和和赋值
函数原型
set<T> st; //默认构造 set(const set &st); //拷贝构造 set& operator=(const set &st); //重载赋值运算符
void printSet(const set<int> &s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(20);
s1.insert(40);
s1.insert(50);
s1.insert(30);
s1.insert(30); //多插入一个30
printSet(s1); //10 20 30 40 50 输出按顺序,且只有一个30
//拷贝构造
set<int> s2(s1);
printSet(s2); // 10 20 30 40 50
//赋值
set<int> s3;
s2 = s2;
printSet(s3);// 10 20 30 40 50
}
set容器在插入数据的时候,没有push_back,只有intsert;即使插入了重复元素,也不会报错,但是插入不会成功,在输出的时候不会输出有重复的元素。
大小和交换
函数原型
size(); //返回容器中元素的数目 empty(); //判断容器是否为空 swap(); //交换两个容器
void printSet(const set<int> &s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(20);
s1.insert(40);
s1.insert(50);
s1.insert(30);
if(s1.empty())
{
cout << "s1为空!" << endl;
}
else
{
cout << "s1不为空,大小为:" << s1.size() << endl;
}
set<int> s2;
s2.insert(60);
s2.insert(80);
s2.insert(70);
s2.insert(40);
s2.insert(30);
cout << "交换前" << endl;
cout << "s1: ";
printSet(s1); //10 20 30 40 50
cout << "s2: ";
printSet(s2); // 30 40 60 70 80
s1.swap(s2);
cout << "交换后" << endl;
cout << "s1: ";
printSet(s1); // 30 40 60 70 80
cout << "s2: ";
printSet(s2); //10 20 30 40 50
}
set容器不支持resize操作。
插入和删除
函数原型
insetr(elem); //在容器中插入元素 clear(); //清除所有元素 erase(pos); //删除pos迭代器所指的元素,返回下一个元素地返回值 erase(begin,end) //删除区间[begin,end)的有元素,返回下一个迭代器 erase(elem);//删除容器中值为elem的元素
void printSet(const set<int> &s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> s1;
s1.insert(20); //插入数据
s1.insert(10);
s1.insert(40);
s1.insert(50);
s1.insert(30);
printSet(s1); //10 20 30 40 50
//删除起始位置的数据
s1.erase(s1.begin());
printSet(s1);//20 30 40 50
//删除30
s1.erase(30);
printSet(s1); // 20 40 50
//清空
s1.clear();
cout << "容器大小为" << s1.size() << endl;
}
erase也有一个重载版本,可以传进来一个元素,对这个元素进行删除,和list里的remove效果一样。
查找和统计
函数原型
find(key); //查找key是否存在,若存在,返回该元素的迭代器,若不存在,返回end()迭代器 count(key); //统计key的个数 (对于set容器,返回值只能是0或1;对于set容器,返回值可以大于1
void printSet(const set<int> &s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> s1;
//插入数据
s1.insert(20);
s1.insert(10);
s1.insert(30);
s1.insert(40);
s1.insert(50);
//查找30所在的位置
set<int>::iterator pos = s1.find(30);
if (pos != s1.end())
{
cout << "找到元素:" << *pos << endl;
}
else
{
cout << "未找到!" << endl;
}
}
void test02()
{
set<int> s1;
//插入数据
s1.insert(10);
s1.insert(20);
s1.insert(30);
s1.insert(20);
s1.insert(20);
//对于set而言,统计的过要么是0 要么是1
int num = s1.count(30);
cout << "30的个数为:" << num << endl;
}
对于set容器而言,cout统计到的结果要么为0,要么为1。
set和multiset的区别
set容器不可以插入重复的数据,而multiset的可以插入重复数据。这是因为前者在插入数据的时候会进行一个检查,插入数据的同时会返回插入结果。但是multiset不会对其进行检查,所以可以插入从复数据。
void printSet(const set<int> &s)
{
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int> s1;
pair<set<int>::iterator, bool> ret = s1.insert(10);
if(ret.second) //插入成功
{
cout << "插入成功!" << endl;
}
else
{
cout << "插入失败!" << endl;
}
ret = s1.insert(10);
if(ret.second) //插入失败
{
cout << "插入成功!" << endl;
}
else
{
cout << "插入失败!" << endl;
}
multiset<int> ms;
ms.insert(10);
ms.insert(10);
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
对于set容器的insert而言,返回值是一个对组。pair<set<int>::iterator, bool>
,第一个是一个迭代器,表示插入元素的位置;第二个是一个布尔类型的数据,表述是否插入成功。而multiset的insert函数的返回值仅仅是一个迭代器,表示是插入元素的位置,没有表示插入是否成功的布尔数据类型。
pair对组
对组是成对出现的一组数据,如果在函数中需要返回两个值,可以考虑使用对组。
两种对组出创建的方式
pair<type, type>p(value1, value2); pair<type,type>p = make_pair(value1,value2);
void test01()
{
//第一种方式
pair<string, int> p1("Tom", 20);
cout << "姓名:" << p1.first << "年龄:" << p1.second << endl;
//第二种方式
pair<string, int> p2 = make_pair("jarry", 30);
cout << "姓名:" << p2.first << "年龄:" << p2.second << endl;
}
对组的使用中.first
表示第一个数据,.second
表示第二个数据。
改变set容器的排序规则
set默认会给插入的据进行升序排列。接下来我们尝试着利用仿函数,把升序排列改为降序排列。
存放内置数据类型
class Compare
{
public:
bool operator()(int v1, int v2) const //VS2019下要加上const
{
return v1 > v2;
}
};
void printSet(const set<int,Compare> &s)
{
for (set<int,Compare>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test01()
{
set<int,Compare> s1;
//插入数据
s1.insert(10);
s1.insert(30);
s1.insert(50);
s1.insert(20);
s1.insert(40);
printSet(s1);
}
这里我在写的时候出现了问题,编译无法通过。原因是我把
Compare
写到了printSet
的下面,而printSet函数里面要用到Compare
,所以编译不通过。当我把Compare
放到printSet下面的时候,问题就解决了。在Visual Studio2019环境下,仿函数后面要加上const属性,否则编译也会不通过。
这里只能使用仿函数来修改排序策略,不能使用回调函数,因为set容器是在元素插入的时候排序,所以要在创建set容器的时候指定排序规则,只能在模板参数列表中添加参数。不可能在模板参数列表上写一个函数名,只能写上类名,所以只能使用仿函数来修改。
存放自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->age = age;
this->name = name;
}
string name;
int age;
};
class Compare
{
public:
bool operator()(const Person &p1, const Person &p2) const
{
return p1.age > p2.age;
}
};
void printSet(const set<Person, Compare> &s)
{
for (set<Person, Compare>::const_iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名:" << it->name << " 年龄:" << it->age << endl;
}
}
void test01()
{
set<Person, Compare> s1;
//插入数据
s1.insert(Person("刘备", 35));
s1.insert(Person("赵云", 30));
s1.insert(Person("张飞", 33));
s1.insert(Person("诸葛亮", 25));
s1.insert(Person("关羽", 34));
printSet(s1);
}
对于自定义数据类型,是一定要自定参数排序规则的。因为set容器在插入的时候就要排序,编译器内部是不知道怎么对自定义类型进行排序的,如果不指定,程序根本无法运行。