
🔥个人主页:guoguoqiang. 🔥专栏:我与C++的爱恋

1. 关联式容器
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque。forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
2. 键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2())
{}
pair(const T1& a, const T2& b) : first(a), second(b)
{}
};
3. 树形结构的关联式容器
根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器
1.set

set是按照一定次序存储元素的容器
. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
. set在底层是用二叉搜索树(红黑树)实现的
注意:
1.与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
2.set中插入元素时,只需要插入value即可,不需要构造键值对。
3.set中的元素不可以重复(因此可以使用set进行去重)。
4.使用set的迭代器遍历set中的元素,可以得到有序序列
5.set中的元素默认按照小于来比较
6.set中查找某个元素,时间复杂度为:l o g 2 n log_2 nlog 2 n
7.set中的元素不允许修改
8.set中的底层使用二叉搜索树(红黑树)来实现
1.set的使用

T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理

构造函数:
(1):构造空的set
(2):用[first, last)区间中的元素构造set
(3):set的拷贝构造
迭代器:

容量:

对元素的修改


在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>

(1):删除set中position位置上的元素
(2):删除set中值为x的元素,返回删除的元素的个数
(3):删除set中[first, last)区间中的元素

交换set中的元素

将set中的元素清空


返回set中值为x的元素的位置

返回set中值为val的元素的个数

在C++中,set 是基于红黑树实现的一个关联容器,它存储有序的不重复元素。这意味着 set 内部元素默认是按照升序排列的,且每个元素值都是唯一的。
set 提供了两个非常有用的成员函数,lower_bound 和 upper_bound,它们用于在有序容器中查找特定元素范围的迭代器。
lower_bound
lower_bound 函数返回一个指向当前set中不小于给定值的第一个元素的迭代器。如果给定值在set中不存在,它将返回指向下一个更大的元素的迭代器;如果给定值大于set中的任何元素,它将返回指向set末尾的迭代器。
换句话说,lower_bound 返回的是指向set中第一个不小于(即大于等于)给定值的元素的迭代器
std::set<int> s;
s.insert(1);
s.insert(3);
s.insert(5);
auto it_lower = s.lower_bound(3); // it_lower 现在指向元素 3
auto it_lower2 = s.lower_bound(4); // it_lower2 现在指向元素 5
upper_bound
upper_bound 函数返回一个指向当前set中大于给定值的第一个元素的迭代器。如果所有的元素都小于给定值,它将返回指向set末尾的迭代器。
upper_bound 返回的是指向set中第一个大于给定值的元素的迭代器。
std::set<int> s;
s.insert(1);
s.insert(3);
s.insert(5);
auto it_upper = s.upper_bound(3); // it_upper 现在指向元素 5
auto it_upper2 = s.upper_bound(5); // it_upper2 现在指向 set 的末尾
1.lower_bound 和 upper_bound 如果给定值不存在于set中,它们不会向 set 添加元素。
2.这两个函数同样适用于 multiset 和 map,multimap 等关联容器,其行为是类似的。
3.它们的返回类型是对应容器的迭代器(或const迭代器,取决于容器实例是否是const)。
4.它们的时间复杂度通常是对数复杂度,也就是O(log n),因为set背后的数据结构是红黑树,它是一种自平衡的二叉搜索树。
在处理范围查询或是在有序集合中寻找下界或上界时,lower_bound 和 upper_bound 函数非常有用
2.map

1.map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
2.在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:typedef pair<const key, T> value_type;
3.在内部,map中的元素总是按照键值key进行比较排序的。
4.map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5.map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6.map通常被实现为二叉搜索树(平衡二叉搜索树(红黑树))


这里的key是不能修改的,用pair键值对存储
这是 insert 方法的精髓所在:它不会覆盖已有的键值对,而是只在键尚未存在时才插入新元素。
这里insert插入的是一个键值对,我们来使用一下:
void test_map1()
{
map<string, string> dict;
pair<string, string> kv1("banana", "香蕉");
dict.insert(kv1);
dict.insert(pair<string, string>("left", "左边"));
dict.insert(make_pair("apple", "苹果"));
dict.insert({ "right" ,"右边" });
}
std::make_pair函数模板被用来创建一个匿名的pair对象,并直接插入到dict中。这个pair对象包含键“apple”和值“苹果”。make_pair有助于减少冗长的类型名称,因为模板类型参数会被自动推导
dict.insert({ "right" ,"右边" });
不是初始化列表的标准用法,这里是构造函数的隐式类型转换。这种方式实际上利用了std::pair的构造函数,它能接收两个参数并将它们转换为一个pair对象。因为std::map的insert方法重载接收一个std::pair<const KeyType, ValueType>类型的对象,编译器可以通过构造函数隐式类型转换,从提供的两个值创建一个pair对象。
当尝试打印迭代器指向的元素时,需要专门引用键(first 成员)和值(second 成员),而不能直接打印迭代器
key不能修改,value可以修改
while (it != dict.end())
{
//std::cout << (*it).first << ": " << (*it).second << " ";
std::cout << it->first << ": " << it->second << " ";
++it;
}

1.键存在于容器中:该函数会返回一个引用,指向与给定键相匹配的映射值。
2.键不存在于容器中:该函数将会插入一个新元素,键为k,并使用映射类型的默认构造函数来初始化它的值。随后函数返回一个引用,指向这个新插入元素的映射值。
通过表达式countMap[e]++自增相应字符串键对应的值。如果e作为键在map中还不存在,map会使用默认构造函数创建一个对应的int值(初始值为0),然后执行++操作将其的值增加到1。如果键已存在,则其对应的值会被自增
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回

可以用count快速判断元素在不在
multiset
multiset 是一个集合容器,它类似于set,但在multiset中相同的元素可以出现多次。multiset中的元素按照特定顺序排列,默认情况下是使用元素类型的 < 运算符来进行升序排列。
特性:
允许键值重复出现。
元素按照键进行自动排序。
直接插入和删除元素具有对数复杂度。
不支持直接通过键访问元素(例如没有 operator[])
#include <iostream>
#include <set>
int main() {
std::multiset<int> ms;
ms.insert(1);
ms.insert(1); // 同样的元素可以被插入多次
ms.insert(2);
for (auto elem : ms) {
std::cout << elem << " "; // 输出: 1 1 2
}
return 0;
}
multimap
multimap 类似于 map,但一个键可以关联多个值。就像 multiset 允许多个相同的元素一样,multimap 允许多个不同的键值对拥有相同的键。
特性:
键可以重复.
元素按照键进行自动排序.
直接插入和删除元素具有对数复杂度.
不支持直接修改键/值

29

被折叠的 条评论
为什么被折叠?



