set容器基本概念
set的特性是,所有元素都会根据元素的键值自动被排序,set的元素不像map那样可以同时拥有实值和键值,set的元素既是键值又是实值,set不允许两个元素有相同的键值。
我们可以通过set的迭代器改变set元素的值吗?不行,因为set元素值就是其键值,关系到set元素的排序规则,如果任意改变set元素值,会严重破坏set组织,换句话说,set的Iterator是一种const_iterator。
set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。
multiset容器基本概念
multiset特性及用法和set容器完全相同,唯一的差别在于它允许键值重复,set和multiset的底层实现是红黑树,红黑树为平衡二叉树的一种。
树的简单知识
二叉树就是任何结点最多只允许有两个子节点,分别是左子节点,和右子节点。
二叉搜索树,是指二叉树中的节点按照一定的规则进行排序,使得对二叉树中元素的访问更加高效。二叉搜索树的放置规则是:任何结点的元素值一定大于其左数中的每一个节点的元素值,并且小于其右子树的值。因此从根节点一直向左走,一直到无路可走,即得到最小值,一直向右走,直至无路可走,可得到最大值,那么在二叉搜索树中找到最大元素和最小元素是非常简单的事情。下图为二叉搜索树。
上面我们介绍了二叉搜索树,那么当一个二叉搜索树的左子树和右子树不平衡的时候,那么搜索依据上图所示,搜索9所花费的时间比搜索17所花费的时间要多,由于我们的输入或者经过我们插入或者删除操作,二叉树失去平衡,造成搜索效率降低。
所以我们有了一个平衡二叉树的概念,所谓的平衡不是指完全平衡
所有的左子节点与右子节点的查找不能大于1.
set常用API
set构造函数:
setst; //set默认构造函数
multisetmst; //multiset默认构造函数
set(const set&st); //拷贝构造函数
set赋值操作
set& operator = (const set& st); //重载等号运算符
swap(st); //交换两个集合容器
set大小操作
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
set插入和删除操作:
insert(elem); //在容器中插入元素
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end); //删除区间(beg,end)的所有元素,返回下一个元素的迭代器
erase(elem); //删除容器中值为elem的元素
set查找操作:
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器,若不存在,返回set.end()
count(key); //查找键key的元素的个数
lower_bound(keyElem); //返回第一个key>=keyElem元素的迭代器
upper_bound(keyElem); //返回第一个key>keyElem元素的迭代器
equal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代器
//An highlighted block
void print(set<int> &s)
{
for(set<int>::iterator it = s.begin();it != s.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
set<int>s1;
//关联式容器 key进行排序,从小到大
s1.insert(5);
s1.insert(1);
s1.insert(7);
s1.insert(3);
s1.insert(9);
print(s1);
if(s1.empty())
{
cout << "s1为空" << endl;
}
else
{
cout << "s1的大小:" << s1.size() << endl;
}
//删除
s1.erase(s1.begin());
s1.erase(3);
print(s1);
}
//An highlighted block
void test2()
{
set<int>s1;
s1.insert(5);
s1.insert(1);
s1.insert(7);
s1.insert(3);
s1.insert(9);
//对于set没有value key就是value
set<int>::iterator pos = s1.find(3);
//判断是否找到
if (pos != s1.end())
{
cout << "找到了,值为:" << *pos << endl;
}
else
{
cout << "未找到" << endl;
}
//查找键key的元素的个数 对于set而言 结果是0或者1
int num = s1.count(1);
cout << "1的个数为:" << num << endl;
//lower_bound(keyElem); //返回第一个key >= keyElem元素的迭代器
set<int>::iterator key_pos = s1.lower_bound(3);
if (key_pos != s1.end())
{
cout << "找到了 lower_bound(3)的值为:" << *key_pos << endl;
}
else
{
cout << "未找到" << endl;
}
//upper_bound(keyElem); //返回第一个key > keyElem元素的迭代器
set<int>::iterator Upper = s1.upper_bound(3);
if (Upper != s1.end())
{
cout << "找到了 upper_bound(3)的值为:" << *Upper << endl;
}
else
{
cout << "未找到" << endl;
}
//equal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代器
//上下限就是lower_bound和upper_bound
pair<set<int>::iterator,set<int>::iterator> ret = s1.equal_range(3);
//获取第一个值
if (ret.first != s1.end())
{
cout << "找到equal_range中lower_bound的值:" << *(ret.first) << endl;
}
else
{
cout << "未找到 " << endl;
}
//获取第二个值
if (ret.second != s1.end())
{
cout << "找到equal_range中upper_bound的值:" << *(ret.second) << endl;
}
else
{
cout << "未找到" << endl;
}
}
//An highlighted block
//创建对组
void test3()
{
//第一种
pair<string,int> p(string("Tom"),100);
//取值
cout << "姓名:" << p.first << endl;
cout << "年龄:" << p.second << endl;
//第二种
pair<string,int> p2 = make_pair("Jerry",200);
cout << "姓名:" << p2.first << endl;
cout << "年龄:" << p2.second << endl;
}
//An highlighted block
//set容器不允许插入重复的键值
//set插入返回值是 pair<迭代器,是否成功指示>
void test4()
{
set<int> s;
s.insert(10);
pair<set<int>::iterator,bool> ret = s.insert(10);
if (ret.second)
{
cout << "第二次插入成功" << endl;
}
else
{
cout << "第二次插入失败" << endl;
}
print(s);
}
//An highlighted block
//指定set排序规则 从大到小
//仿函数
class myCompare
{
public:
//重载()
bool operator() (int val,int val2)
{
return val > val2;
}
};
//set容器排序
void test5()
{
set<int>s1;
s1.insert(5);
s1.insert(1);
s1.insert(7);
s1.insert(3);
s1.insert(9);
print(s1);
//从大到小排序
//在插入前就指定排序规则
set<int,myCompare> s2;
s2.insert(5);
s2.insert(1);
s2.insert(8);
s2.insert(4);
s2.insert(-1);
for (set<int,myCompare>::iterator it = s2.begin();it != s2.end();it++)
{
cout << *it << " ";
}
cout << endl;
}
//An highlighted block
//自定义数据类型
class Person
{
public:
Person(string name,int age)
{
this ->_name = name;
this ->_age = age;
}
string _name;
int _age;
};
class _compare_person
{
public:
bool operator()(const Person & p1,const Person & p2)
{
if (p1._age < p2._age) //年龄升序
{
return true;
}
return false;
}
};
void test6()
{
//插入自定义数据类型,要先指定好排序规则
set<Person,_compare_person> s;
Person p1("tom",20);
Person p2("jerry",30);
Person p3("bob",18);
Person p4("hanmeimei",14);
Person p5("tom",23);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
//实验发现一个有趣的现象,若是按照年龄排序,则年龄相同的无法插入,但是姓名相同年龄不同的可以插入
pair<set<Person,_compare_person>::iterator,bool> ret = s.insert(p5);
if (ret.second)
{
cout << "yes" << endl;
}
else
{
cout << "No" << endl;
}
for (set<Person,_compare_person>::iterator it = s.begin();it != s.end();it++)
{
cout << "姓名:" << it ->_name << " 年龄:" << it ->_age << endl;
}
}