【C++】map 容器的使用
文章目录
- 上文链接
- 一、参考文档
- 二、pair 类型
- 1. pair 介绍
- 2. 创建与初始化
- 3. 访问成员
- 三、map 介绍
- 四、map 构造函数与迭代器
- 1. 无参默认构造
- 2. 列表构造
- 3. 迭代器区间构造
- 4. 拷贝构造
- 5. 迭代器
- 五、map 的增删查改
- 1. insert 插入数据
- 2. erase 删除数据
- 3. find 与 count
- 4. lower_bound 与 upper_bound
- 5. [ ] 实现插入与修改
- 六、multimap 与 map 的区别
上文链接
- 【C++】set 容器的使用
一、参考文档
- map 与 multimap 的使用
二、pair 类型
1. pair 介绍
pair 是 STL 中一个重要的模板类,它可以用于将两个不同类型的值组合成一个单一对象。它定义在头文件 <utility>
中(<map>
等头文件会间接包含它)。
有的时候函数想要返回两个值的时候,我们就可以把这两个值放在一个 pair 中储存并返回。pair 的底层是一个结构体 struct
,其包含两个数据成员:
first
:pair 中的第一个数据。second
:pair 中的第二个数据。
这两个成员是公有的,可直接访问和修改。并且这两个数据的类型可以是任意的,比如:pair<int, string>
,pair<float, vector<bool>>
等。
2. 创建与初始化
// 方式1:默认构造函数(值初始化)
std::pair<int, double> p1; // p1.first=0, p1.second=0.0// 方式2:直接初始化
std::pair<std::string, int> p2("Alice", 25);// 方式3:使用 make_pair(自动类型推导)
auto p3 = std::make_pair(3.14, "Pi");// 方式4:C++11统一初始化
std::pair<int, char> p4 = { 65, 'A' };
3. 访问成员
std::pair<int, std::string> student(101, "Bob");// 直接访问公有成员
int id = student.first; // 101
std::string name = student.second; // "Bob"// 修改值
student.second = "Robert";
三、map 介绍
STL 中的 map 容器是一个非常重要的关联式容器,它以键值对 (key-value) 的形式存储元素。并且它的底层是一棵红黑树,也就是一个平衡的二叉搜索树,内部的默认比较规则是按照键 (key) 的升序进行的。
map 中存储的每一个键值对是一个 pair 类型:std::pair<const Key, T>
。Key
是键的类型,T
是值的类型。比如:<"apple", 苹果>
,<“banana”, “香蕉”>
。
除此之外,map 中的每一个键只能有一个,如果一个键已经存在了,再插入一个相同的键就会失败。
四、map 构造函数与迭代器
注:下面是 map 类中一些类型的原型。
key_type -> The first template parameter (Key) mapped_type -> The second template parameter (T) value_type -> pair<const key_type,mapped_type>
1. 无参默认构造
// empty 无参默认构造
explicit map (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
// 示例
map<string, int> m1;
2. 列表构造
// initializer list 列表构造
map (initializer_list<value_type> il,
// 示例
map<string, int> score =
{{"张三", 121},{"李四", 127},{"王五", 120}
};for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
李四:127
王五:120
张三:121
注:上面的输出结果的顺序是按照键的字典序排升序时候的结果。
3. 迭代器区间构造
// range 迭代器区间构造
template <class InputIterator>
map (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());
// 示例
map<string, int> score =
{{"张三", 121},{"李四", 127},{"王五", 120}
};map<string, int> score2(score.begin(), score.end());for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
李四:127
王五:120
张三:121
4. 拷贝构造
// copy 拷⻉构造
map (const map& x);
// 示例
map<string, double> m1({ { "pi", 3.14 }, {"e", 2.718} });
map<string, double> m2(m1);for (auto& e : score2) cout << e.first << ":" << e.second << endl;
// 输出
e:2.718
pi:3.14
5. 迭代器
// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type// 正向迭代器
iterator begin();
iterator end();// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();
五、map 的增删查改
1. insert 插入数据
- 单个数据插入,如果 key 已经存在则插入失败,key 存在但 value 不相等也会插入失败。
pair<iterator,bool> insert (const value_type& val);
这个函数的返回值是一个 pair 类型,分为两种情况:
- 如果插入成功,返回
<插入的该数据对应的迭代器, true>
; - 如果插入失败,返回
<已经存在的该数据对应的迭代器, false>
。
// 示例
map<string, string> dict =
{{"left", "左边"}, {"right", "右边"},{"insert", "插入"},{ "string", "字符串" }
};auto it = dict.begin();
while (it != dict.end())
{// cout << (*it).first << ":"<< (*it).second << endl;cout << it->first << ":" << it->second << endl;++it;
}
cout << endl;// insert 插入 pair 对象的 4 种⽅式,对比之下,最后一种最方便
pair<string, string> kv1("first", "第一个");
dict.insert(kv1);
dict.insert(pair<string, string>("second", "第二个"));
dict.insert(make_pair("sort", "排序"));
dict.insert({ "auto", "自动的" });// "left" 已经存在,插入失败
dict.insert({ "left", "左边;剩余" });for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;
// 输出
insert:插入
left:左边
right:右边
string:字符串auto:自动的
first:第一个
insert:插入
left:左边
right:右边
second:第二个
sort:排序
string:字符串
- 列表插入,已经在容器中存在的不会插入。
void insert (initializer_list<value_type> il);
// 示例
map<string, int> inventory =
{{"Apple", 50},{"Banana", 30}
};inventory.insert(
{{"Orange", 40},{"Grapes", 60}
});
- 迭代器区间插入,已经在容器中存在的不会插入。
void insert (InputIterator first, InputIterator last);
// 示例
map<string, int> inventory =
{{"Apple", 50},{"Banana", 30}
};std::map<std::string, int> discountItems =
{{"Banana", 10}, // 重复键,不会插入{"Watermelon", 5}
};inventory.insert(discountItems.begin(), discountItems.end());
2. erase 删除数据
- 删除元素
k
,k
不存在返回 0,存在则返回 1。
size_type erase (const key_type& k);
- 删除一个迭代器位置的值,返回被删除位置下一个位置的迭代器。
iterator erase (const_iterator position);
- 删除一段迭代器区间的值。
iterator erase (const_iterator first, const_iterator last);
示例:
map<string, string> dict =
{{"left", "左边"},{"right", "右边"},{"insert", "插入"},{ "string", "字符串" },{ "auto", "自动的" }
};for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;dict.erase("right");
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;auto it1 = dict.find("string");
dict.erase(it1);
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;auto it2 = dict.find("insert");
dict.erase(it2, dict.end());
for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
cout << endl;
auto:自动的
insert:插入
left:左边
right:右边
string:字符串auto:自动的
insert:插入
left:左边
string:字符串auto:自动的
insert:插入
left:左边auto:自动的
3. find 与 count
find
:查找键k
,找到了返回对应的迭代器,没有找到返回end()
。
iterator find (const key_type& k);
// 示例
map<string, string> dict =
{{"left", "左边"},{"right", "右边"},{"insert", "插入"},{ "string", "字符串" }
};string s;
cin >> s;auto it = dict.find(s);
if (it != dict.end())
{dict.erase(it);cout << "删除成功" << endl;for (const auto& e : dict) cout << e.first << ":" << e.second << endl;
}
else
{cout << s << "不存在" << endl;
}
// 输入1
right
// 输出1
删除成功
insert:插入
left:左边
string:字符串// 输入2
auto
// 输出2
auto不存在
count
:查找键k
,返回其个数。
size_type count (const key_type& k) const;
4. lower_bound 与 upper_bound
lower_bound
:返回大于等于k
位置的迭代器。upper_bound
:返回大于k
位置的迭代器。
iterator lower_bound (const key_type& k);
const_iterator lower_bound (const key_type& k) const;iterator upper_bound (const key_type& k);
const_iterator upper_bound (const key_type& k) const;
// 示例
map<char, int> m =
{{ 'a', 20 },{ 'b', 15 },{ 'c', 30 },{ 'd', 50 },{ 'e', 40 }
};auto it_l = m.lower_bound('b');
auto it_u = m.upper_bound('d');cout << it_l->first << ":" << it_l->second << endl;
cout << it_u->first << ":" << it_u->second << endl;
// 输出
b:15
e:40
5. [ ] 实现插入与修改
在 map 中是不允许修改 key 的,因为 map 的底层是一棵二叉搜索树,它是按照 key 来进行排序的,修改了 key 就破坏了搜索树的结构。但是我们可以修改 key 对应的 value,如何修改?。
其中一种修改的方式时通过迭代器,用迭代器遍历或者用 find 返回 key 所在的 iterator 进行修改,map 还有一个非常重要的修改接口 operator[]
,但是 operator[]
不仅可以持修改对应的 value,还支持插入数据和查找数据,所以他是一个多功能复合接口。
map<string, int> price
{{"苹果", 20},{"香蕉", 30},{"橙子", 15},{"西瓜", 35}
};for (const auto& e : price) cout << e.first << ":" << e.second << endl;
cout << endl;// 修改已经存在的数据
price["苹果"] += 5; // 修改
cout << "苹果:" << price["苹果"] << endl << endl;// 如果不存在,则会自动插入。若没有赋值,则插入后的对应值为该类型的默认值,如: int 为 0,double 为 0.0
price["芒果"]; // 插入
for (const auto& e : price) cout << e.first << ":" << e.second << endl;
cout << endl;price["葡萄"] = 30; // 插入 + 修改
for (const auto& e : price) cout << e.first << ":" << e.second << endl;
// 输出
橙子:15
苹果:20
西瓜:35
香蕉:30苹果:25橙子:15
芒果:0
苹果:25
西瓜:35
香蕉:30橙子:15
芒果:0
苹果:25
葡萄:30
西瓜:35
香蕉:30
六、multimap 与 map 的区别
multimap 和 map 的使用基本是一样的,主要区别点在于 multimap 支持关键值 key 的重复,那么 insert/find/count/erase 都围绕着支持关键值 key 重复有所差异,这里跟 set 和 multiset 完全一样。比如 find时,有多个 key,返回中序遍历的第一个 key。其次就是 multimap 不支持 []
。