纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。
💬文章目录
一.对组(pair)
在STL中有些容器的元素是一种叫pair的数据结构。
对组(pair)是类模板,对组(pair)将一对值组合成一个值,一般用于表示key/value数据,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性first和second访问。其实现是结构体。
什么是key/value?
在日常生活中,有很多数据集,不管什么样的数据集都可以用key/value表示,key也叫键值,value也叫实值。
- 例如:QQ号是key,生日年龄等级昵称都是value。
- 全国人口数据,身份证号是key,性别出生日期都是value
key的特点是不会重复,我们的QQ号身份证号肯定不会重复,value可以是一个数据项也可以是多个数据项。
pair结构模板的定义如下:
template <class T1, class T2>
struct pair
{
T1 first; // 第一个成员,一般表示key。
T2 second; // 第二个成员,一般表示value。
pair(); // 默认构造函数。
pair(const T1 &val1,const T2 &val2); // 有两个参数的构造函数。
pair(const pair<T1,T2> &p); // 拷贝构造函数。
void swap(pair<T1,T2> &p); // 交换两个pair。
};
make_pair函数模板的定义如下:
template <class T1, class T2>
make_pair(const T1 &first,const T2 &second)
{
return pair<T1,T2>(first, second);
}
实例:对组pair的使用
#include <iostream>
using namespace std;
void test()
{
//第一种方法创建一个对组
pair<string, int> pair1(string("强风吹拂"), 20); // 有两个参数的构造函数。
cout << pair1.first << endl; //访问pair第一个值
cout << pair1.second << endl;//访问pair第二个值
//第二种
pair<string, int> pair2 = make_pair("king", 20);//make_pair函数创建pair对组,make_pair会返回一个临时的pair
cout << pair2.first << endl;
cout << pair2.second << endl;
//pair=赋值
pair<string, int> pair3 = pair2;
cout << pair3.first << endl;
cout << pair3.second << endl;
}
int main()
{
test();
return 0;
}
运行结果:
二.map/multimap基本概念
map的特性是,所有元素都会根据元素的键值key自动排序。map所有的元素都是pair,同时拥有键值key和实值value,pair的第一元素被视为键值key,第二元素被视为实值value,map不允许两个元素有相同的键值。map和multimap都都封装了红黑树(平衡排序二叉树),用于查找。
map容器的迭代器:
红黑树采用二叉链表实现,所以map能提供了双向迭代器。
二叉链表:
struct BTNode
{
pair<K,V> p; // 键值对。
BTNode *parent; // 父节点。
BTNode *lchirld; // 左子树。
BTNode *rchild; // 右子树。
};
我们可以通过map的迭代器改变map的键值吗?答案是不行,因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。
multimap和map的操作类似,唯一区别multimap键值可重复。
map类模板的声明:
template <class K, class V, class P = less<K>, class _Alloc = allocator<pair<const K, V >>>
class map : public _Tree<_Tmap_traits< K, V, P, _Alloc, false>>
{
…
}
- 第一个模板参数K:key的数据类型(pair.first)。
- 第二个模板参数V:value的数据类型(pair.second)。
- 第三个模板参数P:排序方法,缺省按less升序。
- 第四个模板参数_Alloc:分配器,缺省用new和delete。
三.map容器常用操作
①map构造函数
注:使用map/multimap容器时,需包含头文件#include <map>
关于构造函数,我们只要知道如何创建一个空的map容器即可,然后往容器中插入数据。插入数据的方法在后面演示。
函数原型 | 解释 |
---|---|
map<T1, T2> mapTT; | map默认构造函数:,T1为key的数据类型,T2为value的数据类型 |
map<T1, T2>(const map &mp); | 拷贝构造函数,T1和T2应和mp相同。 |
②map迭代器获取
map能提供了双向迭代器。
获取正向迭代器iterator:
函数原型 | 解释 |
---|---|
iterator begin(); | 返回指向开始位置的迭代器,iterator是正向迭代器。只能使用++运算符从左向右遍历容器,每次沿容器向右移动一个元素。 |
const_iterator begin(); | 返回指向开始位置并且为常量的迭代器 |
const_iterator cbegin(); | 返回指向开始并且为常量的迭代器,const_iterator 常正向迭代器。函数作用:配合auto使用。 |
iterator end(); | 返回指向末尾元素的下一个位置的迭代器 |
const_iterator end(); | 返回指向末尾元素的下一个位置并且为常量的迭代器 |
const_iterator cend(); | 返回指向末尾元素的下一个位置的并且为常量的迭代器,函数作用:配合auto使用。 |
获取反向迭代器reverse_iterator:
函数原型 | 解释 |
---|---|
reverse_iterator rbegin(); | 返回反向迭代器,指向末尾元素下一个位置,操作都是往相反反向,reverse_iterator 为反向迭代器。 |
const_reverse_iterator crbegin(); | 返回反向迭代器,指向末尾元素下一个位置,操作都是往相反反向,并且为常量属性,const_reverse_iterator 常反向迭代器。 |
reverse_iterator rend(); | 返回反向迭代器,指向开头元素的位置,操作都是往相反反向 |
const_reverse_iterator cre nd(); | 返回反向迭代器,指向开头元素的位置,操作都是往相反反向,并且为常量属性 |
迭代器都可以进行++
操作。反向迭代器和正向迭代器的区别在于:
对正向迭代器进行++
操作时,迭代器会指向容器中的后一个元素;
而对反向迭代器进行++
操作时,迭代器会指向容器中的前一个元素。
注意:begin函数和cbegin函数都可以返回const_iterator,那么为什么要两个函数呢?
因为begin函数有重载,无法配合auto(自动推导数据类型)使用,所以才多出一个cbegin函数。
③map赋值操作
函数原型 | 解释 |
---|---|
map&operator=(const map &mp); | 重载等号操作符 |
swap(mp); | 交换两个集合容器 |
④map大小操作
函数原型 | 解释 |
---|---|
size(); | 返回容器中元素的数目 |
empty(); | 判断容器是否为空 |
⑤map插入数据元素操作(重点)
函数原型 | 解释 |
---|---|
void insert(…); | 用统一初始化列表在容器中插入多个元素。 |
pair<iterator,bool> insert(…); | 往容器插入一个元素,返回pair<iterator,bool>,pair.first是已插入元素的迭代器,pair.second是插入结果。 |
void insert(iterator first,iterator last); | 用迭代器插入一个区间的元素,有的可能成功,有的可能失败,所以返回值为void |
实例:map插入数据元素操作
#include <iostream>
using namespace std;
#include <map>//包含头文件
void test()
{
map<string, string> m;
//void insert(……)函数举例,
m.insert({ pair<string,string>("08","亚索"),make_pair("03", "瑟提"),map<string, string>::value_type("01","德莱文") ,{"04","卢锡安"} });
//pair<iterator, bool> insert(…);函数举例
map<string, string> m2;
// 第一种 通过创建pair匿名对象的方式插入对象
auto ret = m2.insert(pair<string, string>("12", "斯维因"));
if (ret.second)
{
cout << "插入成功" << endl;
}
else
{
cout << "插入失败" << endl;
}
// 第二种 通过make_pair的方式插入对象,make_pair会返回一个临时对象
m2.insert(make_pair("05", "皇子"));
// 第三种 通过value_type的方式插入对象
m2.insert(map<string, string>::value_type("14", "塞恩"));
// 第四种 通过数组的方式插入值
m2["09"] = "强风吹拂king";//[]中括号内是键值
for (auto& val : m)
{
cout << val.first << "," << val.second << " ";
}
cout << endl;
for (auto& val : m2)
{
cout << val.first << "," << val.second << " ";
}
cout << endl;
}
int main()
{
test();
return 0;
}
⑥元素操作
函数原型 | 解释 |
---|---|
operator[key]; | 用给定的key访问元素。 |
at(key); | 用给定的key访问元素。 |
注意:
①[ ]运算符:如果指定键值不存在,会向容器中添加新的键值对,并且value为空。如果指定键值存在,则读取或修改容器中指定键的值。
②at()成员函数:如果指定键值不存在,不会向容器中添加新的键值对,而是直接抛出out_of_range 异常。
实例:元素操作
#include <iostream>
using namespace std;
#include <map>//包含头文件
void test()
{
map<string, string> m({ { "08","亚索" }, { "03","伊泽瑞尔" }, { "01","寒冰" }, { "07","猪妹" }, { "05","德莱文" } });
cout << "m[08]=" << m["08"] << endl; // 显示key为08的元素的value。
cout << "m[09]=" << m["09"] << endl; // 显示key为09的元素的value。key为09的元素不存在,将添加新的键值对。并且value为空。
m["09"] = "大头";
m["07"] = "女皇"; // 把key为07的元素的value修改为女皇。
m["12"] = "强风吹拂king"; // 通过[]将添加新的键值对。
for (auto& val : m)
{
cout << val.first << "," << val.second << " ";
}
cout << endl;
}
int main()
{
test();
return 0;
}
⑦map删除操作
函数原型 | 解释 |
---|---|
clear(); | 删除所有元素 |
erase(pos); | 删除pos迭代器所指的元素,返回下一个元素的迭代器。 |
erase(beg,end); | 删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。 |
erase(keyElem); | 删除容器中键值key为keyElem的对组。 |
⑧map查找操作
函数原型 | 解释 |
---|---|
find(key); | 查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end(); |
count(keyElem); | 返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1。 |
lower_bound(keyElem); | 返回一个指向当前 map容器中第一个大于或等于keyElem 的元素的双向迭代器。如果 map容器用 const 限定,则该方法返回的是 const 类型的双向迭代器,若不存在,返回map.end(); |
upper_bound(keyElem); | 返回一个指向当前 map容器中第一个大于keyElem 的元素的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。若不存在,返回map.end(); |
equal_range(keyElem) | 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为keyElem 的元素(map容器中各个元素是唯一的,因此该范围最多包含一个元素)。若不存在,返回map.end(); |
实例:map查找操作
#include <iostream>
using namespace std;
#include <map>//包含头文件
void test()
{
map<string, string> m({ { "08","亚索" }, { "03","伊泽瑞尔" }, { "01","寒冰" }, { "07","猪妹" }, { "05","德莱文" } });
for (auto& val : m)
cout << val.first << "," << val.second << " ";
cout << endl;
// 在map容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。
auto it1 = m.find("05");
if (it1 != m.end())
{
cout << "查找成功:" << it1->first << "," << it1->second << endl;
}
else
{
cout << "查找失败。\n";
}
//lower_bound返回一个指向当前 map容器中第一个大于或等于keyElem 的元素的双向迭代器。
//如果 map容器用 const 限定,则该方法返回的是 const 类型的双向迭代器若不存在,返回map.end();
auto it2 = m.lower_bound("05");
if (it2 != m.end())
{
cout << "lower_bound()查找成功:" << it2->first << "," << it2->second << endl;
}
else
{
cout << "lower_bound()查找失败。\n";
}
//upper_bound返回一个指向当前 map容器中第一个大于keyElem 的元素的迭代器。
//如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。若不存在,返回map.end();
auto it3 = m.upper_bound("05");
if (it3 != m.end())
{
cout << "upper_bound()查找成功:" << it3->first << "," << it3->second << endl;
}
else
{
cout << "upper_bound()查找失败。\n";
}
//equal_range返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价
// pair.second 和 upper_bound() 方法的返回值等价。若不存在,返回map.end();
//返回大于等于05的两个最小的数的迭代器,如果有05那么就返回05的迭代器和大于05的最小数的迭代器
//用对组的方式存储起来
auto ret = m.equal_range("05");
if(ret.first!=m.end())
{
cout << "equal_range查找成功:" << endl;
cout << ret.first->first << ret.first->second << endl;
cout << ret.second->first << ret.second->second << endl;
}
else
{
cout << "equal_range查找失败。\n";
}
// 统计map容器中键值为key的键值对的个数。
cout << "count(05)=" << m.count("05") << endl; // 返回1。
cout << "count(06)=" << m.count("06") << endl; // 返回0。
}
int main()
{
test();
return 0;
}