书接上期,这一期我们聊聊关联式容器set和multiset,关联式容器与前面我们学习的顺序容器还是有很大的区别

set为单集合容器,底层数据结构为红黑树
所有元素都会在插入set时自动被排序
举个例子,如下有 2 组键值对数据:
{<'a', 1>, <'b', 2>, <'c', 3>}
{<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}
显然,第一组数据中各键值对的键和值不相等,而第二组中各键值对的键和值对应相等。对于 set 容器来说,只能存储第 2 组键值对,而无法存储第一组键值对。
基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。仍以存储上面第 2 组键值对为例,只需要为 set 容器提供 set
注意:set与multiset唯一的区别是
- set不允许容器中有重复的元素
- multiset允许
例如一组数据{1,3,2,5,5,}存入set后,set里面存的是{1,2,3,5}//自动排序,且不存重复元素
而对于multiset则存入{1,2,3,5,5}//自动排序,可以存入相同元素
首先要清楚set 容器类模板中未提供 at() 成员函数,也未对 [] 运算符进行重载。因此,要想访问 set 容器中存储的元素,只能借助 set 容器的迭代器。
STL 标准库为 set 容器配置的迭代器类型为双向迭代器。这意味着,假设 p 为此类型的迭代器,则其只能进行 ++p、p++、--p、p--、*p 操作,并且 2 个双向迭代器之间做比较,也只能使用 == 或者 != 运算符。
- set
st;//默认构造 - set(const set &st);//拷贝构造
- set& operator=(const set& st);//重载等号赋值
- template<typename T>
- void Show(set
& st) - {
- for (auto it = st.begin(); it != st.end(); ++it)
- {
- cout <<(*it)<<" ";
- }
- cout << endl;
- }
-
- int main()
- {
- set<int>st;
- //插入数据只有insert方式
- st.insert(1);
- st.insert(2);
- st.insert(3);
- st.insert(4);
- Show(st);
-
- cout << "-----------------" << endl;
- set<int>st1;//set特点,所有元素插入时自动被排序
- st1.insert(5);
- st1.insert(10);
- st1.insert(3);
- st1.insert(1);
- Show(st1);
-
- cout << "-----------------" << endl;
- set<int>st2;//set容器不允许插入重复值
- st2.insert(10);
- st2.insert(3);
- st2.insert(3);
- Show(st2);
-
- cout << "-----------------" << endl;
- set<int>st3(st2);//拷贝构造
- Show(st3);
-
- cout << "-----------------" << endl;
- set<int>st4;//等号赋值
- st4 = st3;
- Show(st4);
- return 0;
-
- }
- template<typename T>
- void Show(multiset
& st) - {
- for (auto it = st.begin(); it != st.end(); ++it)
- {
- cout <<(*it)<<" ";
- }
- cout << endl;
- }
-
- int main()
- {
- //允许插入重复值
- multiset<int>st{1,5,3,3,3,4,2};//用初始化列表初始化
-
- Show(st);
- return 0;
- }
- size();//返回容器中元素的数目
- empty();//判断容器是否为空
- swap(st);//交换两个集合容器
- template<typename T>
- void Show(set
& st) - {
- for (auto it = st.begin(); it != st.end(); ++it)
- {
- cout <<(*it)<<" ";
- }
- cout << endl;
- }
-
- int main()
- {
- set<int>st{1,5,3,4,2};//用初始化列表初始化
- if (st.empty())
- {
- cout << "为空" << endl;
- }
- else
- cout << "不空" << endl;
- Show(st);
- cout << st.size()<
-
- set<int>st1{8,1,5,2};
- st.swap(st1);
- cout << "交换后st:";
- Show(st);
- cout << "交换后st1:";
- Show(st1);
- return 0;
-
- }
set插入和删除
- insert(elem);//在容器中插入元素
- clear();//清除所有元素
- erase(pos);//删除pos迭代器所指的元素,返回下一元素的迭代器
- erase(beg,end);//删除区间[beg,end]的所有元素,返回下一元素迭代器
- erase(elem);//删除容器中elem值元素 类似list中remove函数
- template<typename T>
- void Show(set
& st) - {
- for (auto it = st.begin(); it != st.end(); ++it)
- {
- cout <<(*it)<<" ";
- }
- cout << endl;
- }
-
- int main()
- {
- set<int>st{1,5,3,4,2};//用初始化列表初始化
- st.insert(15);
- Show(st);
- st.erase(st.begin(), ++st.begin() );//因为不能随机访问,同样没有重载+或-
- Show(st);
-
- st.erase(3);//删除3
- Show(st);
-
- return 0;
-
- }
set查找和统计
- find(key);//查找key是否存在,若存在返回该键的迭代器,不存在返回set.end()位置
- count(key);//统计key的元素个数;对于set只有0或1 因为set不允许插入重复数据
- int main()
- {
- set<int>st{1,5,3,4,2};//用初始化列表初始化
-
-
- set<int>::iterator ot = st.find(5);
- if (ot != st.end())
- {
- cout << "找到元素:" << (*ot) << endl;
- }
- else
- cout << "没找到元素:" << (*ot) << endl;
-
- //统计1的个数
- cout << st.count(1) << endl;
- return 0;
-
- }
这是标准库里迭代器部分的内容,简单点说,就是用find这个函数,去找str这个序列中的i元素,如果序列中所找的这个元素不存在,就会返回end()。
那么按着这个思路去理解这两行命令就很容易了!
如果str.find(i)返回的不是str.end(),就说明在str序列中找到i元素:
str.find(i) != str.end() //说明找到了
同理,如果str.find(i)返回的是str.end(),就说明在str序列中没找到i元素:
str.find(i) == str.end() //说明没到
set自定义规则排序(利用仿函数)内置类型
因此set容器的特性,在容器创建是内部元素自动会排序,那么如果我们想要利用set保存我们自己想要的排序顺序该如何操作呢?
- //set容器默认排序为从小到大,掌握如何改变排序规则
- //利用仿函数,可以改变排序规则
- #include
- #include
- using namespace std;
- //仿函数:用类调用函数
- //仿函数设计:
- class MyCompare {
- public:
- //下一行末尾加上const
- /*bool operator()(int v1, int v2){
- return v1 > v2;
- }*/
- bool operator()(int v1, int v2)const {
- return v1 > v2;
- }
- };
- void test01() {
- set<int>s1;
- s1.insert(10);
- s1.insert(40);
- s1.insert(30);
- s1.insert(50);
- s1.insert(20);
- for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {
- cout << *it << " ";
- }
- cout << endl;
-
- //指定排序规则为从大到小
- //重新指定第一步,更改第二个默认值
- //注意:这里第二个默认值需求为类型,不可以是函数(函数名),故使用仿函数
- //set
> s2; // 从大到小排序,也可以这样创建,使用内置的仿函数 -
- set<int,MyCompare>s2;
- s2.insert(10);
- s2.insert(40);
- s2.insert(30);
- s2.insert(50);
- s2.insert(20);
- for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
- cout << *it << " ";
- }
- cout << endl;
- }
- int main() {
- test01();
- }
对于自定义类型的排序
- class Person {
- public:
- Person(string name, int age) {
- this->m_Name = name;
- this->m_Age = age;
- }
-
- string m_Name;
- int m_Age;
- };
-
- class comparePerson {
- public:
- bool operator()(const Person& p1, const Person& p2)const {
- //按照年龄 降序
- return p1.m_Age > p2.m_Age;
- }
- };
-
- void test01() {
- //自定义数据类型 都会指定排序规则
-
- set
s; -
- Person p1("刘备", 28);
- Person p2("关羽", 26);
- Person p3("张飞", 24);
- Person p4("马超", 22);
- Person p5("赵云", 21);
- s.insert(p1);
- s.insert(p2);
- s.insert(p3);
- s.insert(p4);
- s.insert(p5);
-
- for (set
::iterator it = s.begin(); it != s.end(); it++) { - cout << "姓名:" << it->m_Name << " " << "年龄:" << it->m_Age << endl;
- }
- }
-
- int main() {
-
-
- test01();
-
- return 0;
- }
有关Pair对组
pair只含有两个元素,可以看作是只有两个元素的结构体。对于成对出现的数据,利用对组可以返回两个数据
应用:
- 代替二元结构体
- 作为map键值对进行插入(下一期会讲map)
在创建pair对象时,必须提供两个类型名,两个对应的类型名的类型不必相同,可以在定义时进行成员初始化。
Pair的创建
- 创建方式
- pair
p(value1,value2) ; - pair
p=make_pair(value1,value2); -
- 头文件
- #include
-
- //1.初始化定义
- pair
int > p("wangyaqi",1);//带初始值的 - pair
int> p;//不带初始值的 -
- //2.赋值
- p = {"wang",18};
-
- int main()
- {
- //数据类型 数据
- pair
int>p("公孙离", 17); - cout << "姓名:" << p.first << " 年岁:" << p.second << endl;
-
- //和结构体类似,first代表第一个元素,second代表第二个元素
-
- pair
int>p1=make_pair("钟馗", 47); - cout << "姓名:" << p1.first << " 年岁:" << p1.second << endl;
- return 0;
- }
头文件中除了提供创建 pair 对象的方法之外,还为 pair 对象重载了 <、<=、>、>=、==、!= 这 6 的运算符,其运算规则是:对于进行比较的 2 个 pair 对象,先比较 pair.first 元素的大小,如果相等则继续比较 pair.second 元素的大小。
注意,对于进行比较的 2 个 pair 对象,其对应的键和值的类型比较相同,否则将没有可比性,同时编译器提示没有相匹配的运算符,即找不到合适的重载运算符。
最后需要指出的是,pair类模板还提供有一个 swap() 成员函数,能够互换 2 个 pair 对象的键值对,其操作成功的前提是这 2 个 pair 对象的键和值的类型要相同。例如:
- #include
- #include
// pair - #include
// string - using namespace std;
- int main() {
- pair
int> pair1("pair", 10); - pair
int> pair2("pair2", 20); - //交换 pair1 和 pair2 的键值对
- pair1.swap(pair2);
- cout << "pair1: " << pair1.first << " " << pair1.second << endl;
- cout << "pair2: " << pair2.first << " " << pair2.second << endl;
- return 0;
- }
执行结果:
- pair1: pair2 20
- pair2: pair 10
用类模板实现Pair
- // 类模板
- template <class T1, class T2>
- class Pair
- {
- public:
- Pair(T1 k, T2 v):m_key(k),m_value(v) {};
- bool operator < (const Pair
& p) const; - private:
- T1 m_key;
- T2 m_value;
- };
-
- // 类模板里成员函数的写法
- template <class T1, class T2>
- bool Pair
::operator < (const Pair &p) const - {
- return m_value < p.m_value;
- }
- int main()
- {
- Pair
int > Astudent("Jay",20); - Pair
int > Bstudent("Tom",21); -
- cout << (Astudent < Bstudent) << endl;
-
- return 0;
- }
- //结果为1 20<21
这一期就到此结束了,感谢观看,订阅此专栏。
