文章目录
- 1.map和multimap容器
- 1.map容器的构造和赋值
- 1.构造函数
- 2.赋值操作
- 2.map容器的大小和交换
- 1.获取大小
- 2.交换内容
- 3.map容器的插入和删除
- 1.插入元素
- 2.删除元素
- 3.注意事项
- 4.map容器的查找和统计
- 1.查找元素
- 2.统计元素
- 3.示例
- 5.map容器的排序
- 1.自定义排序规则
- 2.注意点
- 3.示例:基于value排序
1.map和multimap容器
在C++ STL(标准模板库)中,map
和multimap
是两种关联容器,它们用于存储键值对,其中每个键都是唯一的,并且与一个特定的值相关联。然而,它们之间存在一些关键区别:
map
-
唯一性:
map
中的键是唯一的,也就是说,一个键只能对应一个值。如果你尝试插入一个键值对,而该键已经存在于map中,那么原有的键对应的值将被新值替换。 -
排序:
map
中的元素按照键的升序(默认情况下)进行排序。这是通过比较键实现的,可以自定义比较函数来改变排序方式。 -
基本操作:主要操作包括插入(
insert
)、删除(erase
)、查找(find
)和访问(直接通过键访问,如map[key]
)元素。
multimap
-
非唯一性:与
map
的主要区别在于,multimap
允许有多个相同的键,每个键可以对应多个不同的值。这意味着当你插入一个键已经存在的键值对时,新值会被添加到该键已有的值集合中,而不是替换原有值。 -
排序:和
map
一样,multimap
中的元素也是根据键进行排序的,可以是升序或根据自定义的比较函数排序。 -
基本操作:除了与
map
共享的基本操作外,由于可能有多个相同键的存在,multimap
的查找操作(如find
)可能会返回一个迭代器范围,表示所有匹配键的值集合。
使用场景
-
map适用于当你需要确保每个键只对应一个值,且经常需要根据键快速查找或更新其关联值的场景。
-
multimap适合那些一个键可能对应多个值,且在查询时你可能关心所有与某一键相关联的值的情况,比如存储学生的分数时,如果有多个学生获得相同的分数,
multimap
能很好地处理这种情况。
1.map容器的构造和赋值
在C++中,std::map
容器提供了多种构造函数和赋值操作,以方便初始化和管理键值对数据。
1.构造函数
-
默认构造函数
std::map<Key, T> m; // 创建一个空的map,默认按键升序排序。
-
拷贝构造函数
std::map<Key, T> m1; // 假设m1已经被初始化 std::map<Key, T> m2(m1); // m2是m1的一个拷贝
-
使用比较函数构造
struct MyCompare {bool operator()(const Key& lhs, const Key& rhs) const {// 自定义比较逻辑return lhs < rhs; // 示例:使用默认比较} }; std::map<Key, T, MyCompare> m(MyCompare()); // 使用自定义比较函数
-
区间构造
std::map<Key, T> m.begin(), m.end()); // 或者使用其他容器/迭代器范围 std::vector<std::pair<Key, T>> vec; // 假设vec已被填充 std::map<Key, T> m(vec.begin(), vec.end());
2.赋值操作
-
拷贝赋值运算符
std::map<Key, T> m1, m2; // 假设m1和m2已经被初始化 m1 = m2; // 将m2的内容赋值给m1
-
初始化列表赋值(C++11及以上)
std::map<Key, T> m = { {key1, value1}, {key2, value2}, ... };
-
区间赋值
std::map<Key, T> m1, m2; // 假设m2已被填充 m1.assign(m2.begin(), m2.end());
-
插入迭代器对
虽然这不是直接的赋值操作,但可以通过迭代器对插入元素来“赋值”或填充map。std::map<Key, T> m; std::vector<std::pair<Key, T>> vec; // 假设vec已被填充 m.insert(vec.begin(), vec.end());
这些构造和赋值方法提供了灵活的方式来创建和管理std::map
容器,满足不同场景下的需求。
```cpp#include <iostream>#include <map>// 使用标准命名空间,简化后续代码中的类型名称using namespace std;/*** 打印map的内容* @param m 一个整数到整数的映射,其内容将被打印*/void printMap(const map<int,int>& m){// 遍历映射中的所有元素,并打印每个元素的键和值for(auto it : m){cout << it.first << " " << it.second << endl;}}int main(){// 初始化一个map,包含两个元素map<int,int> m = {{1,2},{2,4}};// 创建一个对m的常量引用,以避免复制map的内容const map<int,int>& m1(m);// 调用函数打印map的内容printMap(m);printMap(m1);// 初始化一个空的map,并将其设置为与m相同的内容map<int,int> m2;m2 = m;printMap(m2);return 0;}```
2.map容器的大小和交换
在C++的STL中,std::map
容器提供了以下与大小和交换相关的成员函数:
1.获取大小
-
size(): 此函数返回
map
中元素的数量,即键值对的总数。std::map<Key, T> myMap; // ... 添加元素到myMap std::cout << "Size of the map: " << myMap.size() << std::endl;
-
empty(): 判断
map
是否为空,如果容器中没有元素,则返回true
,否则返回false
。if (myMap.empty()) {std::cout << "The map is empty." << std::endl; } else {std::cout << "The map is not empty." << std::endl; }
2.交换内容
-
swap(std::map<Key, T>& other): 此函数交换两个
map
容器的内容,交换操作高效执行,不会涉及元素的复制或移动。std::map<Key, T> myMap1, myMap2; // ... 分别填充myMap1和myMap2 myMap1.swap(myMap2); // 现在myMap1包含myMap2原来的内容,反之亦然
这些函数允许检查容器的当前状态(是否为空,包含多少元素)以及重新分配两个容器的内容,而无需创建新的容器副本。这对于优化内存使用和性能特别有用。
//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>// 使用标准命名空间,简化后续代码中标准库类型的引用
using namespace std;/*** 打印map的内容* @param m 一个整数到整数的映射,其内容将被打印*/
void printMap(const map<int,int>& m){// 遍历map,打印每个键值对for(auto it : m){cout << it.first << " " << it.second << endl;}
}int main(){// 初始化一个map,包含两个元素map<int,int> m{{1,2},{2,4}};// 调用函数打印map内容printMap(m);// 打印map的大小cout << m.size() << endl;// 检查map是否为空,如果不为空,则再次打印其内容if(!m.empty()){printMap(m);}// 初始化另一个空的mapmap<int,int> m1;// 交换两个map的内容m.swap(m1);// 分别打印交换后的两个map的内容printMap(m);printMap(m1);return 0;
}
3.map容器的插入和删除
在C++的STL中,std::map
容器提供了丰富的成员函数来进行元素的插入和删除操作。以下是几种常见的插入和删除方法:
1.插入元素
-
insert(const value_type& val): 插入一个键值对。如果键已存在,则不会插入重复的键。
myMap.insert(std::make_pair(key, value)); // 或者使用直接初始化语法(C++11起) myMap.insert({key, value});
-
insert(iterator hint, const value_type& val): 尝试在迭代器
hint
指向的位置附近插入元素,以提高效率。如果hint
恰好指向要插入位置的下一个位置,则插入操作可能更快。auto it = myMap.lower_bound(key); myMap.insert(it, std::make_pair(key, value));
-
insert(initializer_list<value_type> ilist): 从初始化列表插入多个键值对(C++11起)。
myMap.insert({ {key1, value1}, {key2, value2}, ... });
2.删除元素
-
erase(iterator position): 删除迭代器
position
指向的元素。auto it = myMap.find(key); if (it != myMap.end()) {myMap.erase(it); }
-
erase(const key_type& key): 删除具有指定键的元素。
myMap.erase(key);
-
erase(iterator first, iterator last): 删除迭代器范围
[first, last)
内的所有元素。auto startIt = myMap.lower_bound(startKey); auto endIt = myMap.upper_bound(endKey); myMap.erase(startIt, endIt);
3.注意事项
- 在删除元素后,之前指向被删除元素的迭代器、指针和引用都会变得无效。
erase
返回的是下一个元素的迭代器,这在连续删除元素时很有用。- 对于
insert
操作,如果试图插入的键已经存在,该操作不会改变容器且不会抛出异常(除非因内存分配失败)。
通过这些方法,你可以有效地管理std::map
容器中的数据,无论是添加新元素还是移除不需要的条目。
//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>// 使用标准命名空间,简化后续代码中标准库类型的引用
using namespace std;/*** 打印map的内容* @param m 一个整数到整数的映射,其内容将被打印*/
void printMap(const map<int,int>& m){// 遍历map,打印每个键值对for(auto it : m){cout << it.first << " " << it.second << endl;}
}int main(){// 初始化一个map,包含两个元素map<int,int> m{{1,2},{2,4}};// 调用函数打印map内容printMap(m);// 打印map的大小cout << m.size() << endl;// 检查map是否为空,如果不为空,则再次打印其内容if(!m.empty()){printMap(m);}// 初始化另一个空的mapmap<int,int> m1;// 交换两个map的内容m.swap(m1);// 分别打印交换后的两个map的内容printMap(m);printMap(m1);return 0;
}
4.map容器的查找和统计
在C++的STL中,std::map
容器提供了几种查找和统计元素的方法:
1.查找元素
-
find(const key_type& k): 返回一个迭代器,指向键为
k
的第一个元素。如果找不到这样的元素,则返回end()
。auto it = myMap.find(key); if (it != myMap.end()) {std::cout << "Found element with key: " << it->first << ", value: " << it->second << std::endl; } else {std::cout << "Element not found." << std::endl; }
-
count(const key_type& k): 返回键为
k
的元素数量。对于map
,由于键唯一,此函数要么返回0(表示不存在),要么返回1(表示存在)。size_t count = myMap.count(key); if (count > 0) {std::cout << "Key exists in the map." << std::endl; } else {std::cout << "Key does not exist in the map." << std::endl; }
2.统计元素
在map
容器中,由于键的唯一性,统计通常较为直接,主要依赖于count
函数来确定某个键是否存在。对于键值对的计数,通常意义不大,因为每个键最多只能对应一个值。但在多值映射multimap
中,count
可以有效统计相同键值出现的次数。
3.示例
假设有一个std::map<int, std::string>
类型的myMap
,以下是如何使用查找和统计方法的简单示例:
#include <iostream>
#include <map>// 使用标准命名空间,简化后续代码中标准库的调用
using namespace std;/*** 打印map中的所有元素* @param m 一个整数到整数的映射,其元素将被打印*/
void printMap(const map<int,int>& m){// 遍历map中的每个元素,并打印其键值对for(auto it : m){cout << it.first << " " << it.second << endl;}
}int main(){// 初始化一个map,包含两个元素:{1, 2} 和 {2, 4}map<int,int> m{{1,2},{2,4}};// 寻找键值为1的元素auto it = m.find(1);cout << it->first << endl;// 检查是否找到键值为1的元素if(it != m.end()){cout << "in" << endl;} else{cout << "no" << endl;}// 调用函数打印map的所有元素printMap(m);// 打印键值为1的元素在map中出现的次数cout << m.count(1) << endl;return 0;
}
5.map容器的排序
std::map
容器在C++ STL中自动保持其元素有序。默认情况下,元素是按照键(key)的升序进行排序的。这种排序是由比较函数决定的,std::map
默认使用std::less<Key>
作为比较器,它提供了一个严格弱序的比较,适用于大多数基本数据类型。
1.自定义排序规则
尽管std::map
默认按key排序,但如果你想改变排序逻辑,可以在创建map
时指定一个自定义的比较函数对象。例如,如果你想让map按照key降序排列,或者基于键的某些复杂属性排序,你可以这样做:
struct CustomCompare {bool operator()(const Key& lhs, const Key& rhs) const {// 自定义比较逻辑,例如降序排序:return rhs < lhs; // 改变比较逻辑以实现降序}
};std::map<Key, Value, CustomCompare> myMap;
2.注意点
- 不可更改排序:一旦
map
实例化,其排序规则就不能再改变。如果需要改变排序,你需要创建一个新的map
实例并使用不同的比较函数。 - 基于value排序:如果需要按照value排序,
std::map
本身不直接支持。一个常见做法是将map
的元素复制到一个std::vector
中,每个元素是一个键值对(例如,使用std::pair<Key, Value>
),然后对这个vector
进行排序。可以使用std::sort
函数配合自定义的比较器来达到目的。
3.示例:基于value排序
std::map<Key, Value> originalMap;
// ... 填充originalMapstd::vector<std::pair<Key, Value>> items(originalMap.begin(), originalMap.end());
std::sort(items.begin(), items.end(), [](const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) {return a.second > b.second; // 降序排序基于value
});// 现在items是按value排序的键值对集合
综上所述,虽然std::map
自动维持其内部元素的排序(基于键),但通过自定义比较函数,可以控制排序逻辑。而对于基于value的排序,则需借助额外的数据结构和排序算法。
//
// Created by 86189 on 2024/7/3.
//
#include <iostream>
#include <map>
using namespace std;// 自定义比较函数对象,用于map中键值的降序排序
class Compare{
public:// 比较两个整数,返回true如果第一个大于第二个bool operator()(const int &a,const int &b) const{return a > b;}
};// 打印map中的所有元素
// 参数m: 使用自定义比较函数对象Compare的map
void printMap(const map<int, int, Compare>& m){// 遍历map中的每个元素并打印for(auto it : m){cout << it.first << " " << it.second <<endl;}
}int main(){// 初始化一个使用自定义比较函数对象的mapmap<int,int,Compare>m{{1,2},{2,4},{3,5}};// 调用函数打印map内容printMap(m);return 0;
}