顺序容器和关联式容器的区别
在学习了标准库STL过程中,我们主要学习已经学习了顺序容器,如vector、list、deque,它们的共同点是通过元素在容器中的位置顺序存储和访问元素,底层为线性序列的数据结构。而本文介绍的set和map容器属于关联式容器,里面存储的是<key, value>结构的键值对,因此通过(key)键存储或访问值,数据检索时效率更高。
概念和使用
set
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
常见用法
构造
std::set<KeyType> mySet; //创建一个空的集合
std::set<KeyType> mySet(beginIterator, endIterator);
//使用迭代器范围 [beginIterator, endIterator) 中的元素来初始化集合
std::set<KeyType> mySet(otherSet);
//拷贝构造,使用其它集合创建当前集合
std::set<KeyType, CompareFunction> mySet;
//自定义比较函数,根据仿函数CompareFunction的规则进行集合元素顺序的排布
插入
由于set集合元素唯一性,插入操作过程中需检查待插入元素在集合中是否已经存在,若存在则插入失败。
返回值为pair对组,包含一个迭代器和一个bool类型的值,迭代器指向拥有该键的元素,bool值表明是否添加成功添加。
mySet.insert(value); // 插入值
mySet.insert(mySet.begin()+1,value); //在指定位置插入值
mySet.insert(beginIterator, endIterator);//插入某容器的指定区间值
删除
删除和插入原理相同,直接列举
mySet.erase(value); // 删除值
mySet.erase(beginIterator, endIterator); // 删除区间值
查找
在集合中查找指定值val,如果找到,返回该值的迭代器位置,如果找不到,返回迭代器end()
auto it = mySet.find(value);
return it == mySet.end()
查找集合中某元素的个数,由于set中元素的唯一性,故查找返回的结果只有1或0
遍历访问
for (const ValueType& value : mySet) {
// 处理 value
}
//也可以写成auto形式
总结:
- 唯一值的集合:std::set 存储的元素是唯一的,不允许重复值。如果尝试插入重复元素,插入操作将被忽略。
- 有序性:在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
- 红黑树实现:通常情况下,std::set 使用红黑树 (Red-Black Tree) 数据结构来实现,这保证了元素的有序性,并且使得插入、删除和查找操作的平均时间复杂度都是 O(log n)。
map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。
key_type、mapped_type 和 value_type 是与该关联容器的键值对结构相关的三个类型,它们有以下区别:
key_type:是 std::map 中键(key)的类型。它表示映射中存储的键的数据类型。键是唯一的,用于标识值(mapped value)。
mapped_type:是 std::map 中值(mapped value)的类型。它表示映射中存储的值的数据类型。在 std::map 中,每个键映射到一个唯一的值,这个值的类型就是 mapped_type。,因为映射的值是字符串。
value_type:value_type 是一个在 std::map 中表示键值对的类型。它通常被定义为一个 std::pair,其中第一个元素是键(key),第二个元素是值(mapped value)。value_type 可以用于表示整个键值对,包括键和值。
例:
现有map<int, string>,key_type 就是 int,mapped_type 就是 string,value_type 就是 pair<const int, string>,其中第一个元素是 const int,表示键的类型,第二个元素是 std::string,表示值的类型。
key_type 表示键的类型,mapped_type 表示值的类型,而 value_type 表示整个键值对的类型。
常见用法
构造
std::map<KeyType, ValueType> myMap; //创建一个空map
std::map<KeyType, ValueType> myMap(beginIterator, endIterator);//使用迭代器范围 [beginIterator,endIterator) 中的元素来初始化map
std::map<KeyType, ValueType> myMap(otherMap);
//拷贝构造,使用其它map创建当前map
std::map<KeyType, ValueType,CompareFunction> myMap;
//自定义比较函数,根据仿函数CompareFunction的规则进行map元素顺序的排布
插入
map的插入set机理相似,不同的是map还有一map::operator[] 的方法插入元素
myMap[key] = value; // 插入键值对
myMap.insert(std::make_pair(key1, value1));//使用insert插入键值对
删除
删除和插入原理相同,直接列举
myMap.erase(key); // 删除键值对
查找
在map中查找指定键,如果找到,返回该键值对的迭代器位置,可以通过迭代器找到键对应的值,如果找不到,返回迭代器end()
auto it = myMap.find(key);
if (it != myMap.end()) {
// 元素存在
ValueType value = it->second;
} else {
// 元素不存在
}
‘[ ]’操作符
根据键查找值,如果map中已经存在此键,则返回键对应的值;如果map中不存在键,会创建一个新的pair,其键为 key,值被默认初始化
multiset和multimap
注意:
- multi:存放的元素可以重复
- multiset作用:可以对元素进行排序
- multimap不支持下标操作
应用场景
set去重:set中元素具有唯一性,插入时自动去重
std::set<int> mySet;
// 插入元素,自动去重
mySet.insert(5);
mySet.insert(2);
mySet.insert(5); // 这个元素已经存在,不会重复插入
mySet.insert(8);
// 遍历集合中的唯一元素
for (const int& value : mySet) {
std::cout << value << " ";
}
//由此我们可以得到一个元素不重复的集合
map字典:键是单词,值是其对应的定义、翻译或其他信息
map计数器:键是元素,值是出现的次数
//字典
std::map<std::string, std::string> dictionary;
dictionary["apple"] = "苹果";
dictionary["banana"] = "香蕉";
//计数器
std::map<int, int> counter;
int numbers[] = {1, 2, 3, 2, 1, 3, 1, 4, 5};
for (int num : numbers) {
counter[num]++;
}