【C++】map|set|原理使用|multiset|multimap|operator[]|

目录

 

一,关联式容器

二,键值对

2.1为什么使用键值对

2.2make_pair()

三,STl关联容器

四,set 

4.1模板参数 

 4.2默认构造

 4.3使用

去重功能和自动排序

4.4增删查

insert

find

erase

五,multiset 

count

erase

find 

六,map 

6.1默认构造 

特性 

6.2使用 

insert

operator[]

示例

方法1

方法2 

 七,multimap


一,关联式容器

  • 在STL中vector,list,stack,queue等都属于序列式容器,底层是线性结构,里面存储的是元素本身;
  • 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是 <key, value> 结构的键值对,在数据检索时比序列式容器效率更高,set map 便是关联式容器 

二,键值对

  键值对是用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量keyvalue

set map 就使用了键值对,这个键值对名为 pair,它是一个 struct 定义的类模板,即可以在外部访问 pair里面的成员变量 

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)
    {}
};

所以pair就是一个类模板,根据<>里的两个参数来实例化类,第一个参数表示key,第二个表示value

  • pair的两个成员变量first表示key,second表示value 

2.1为什么使用键值对

为什么不直接在容器中定义 key value 变量,而是将 key value 合并到 pair 中整体作为一个类型来使用呢?

        这是因为 C++ 一次只能返回一个值,如果我们将 key value 单独定义在容器中,那么我们就无法同时返回 key value ;而如果我们将 key value 定义到另一个类中,那我们就可以直接返回 pair ,然后再到 pair 中分别去取 first second 即可。

2.2make_pair()

该函数是用来制作一个pair类型的匿名对象的:

make_pair - C++ Reference (cplusplus.com)

	map<string, string> dict;
	pair<string, string> kv1("sort", "排序");
	//三种插入
	
	dict.insert(kv1);		//有名
	dict.insert(pair<string,string>("left", "左边"));	//匿名
	dict.insert(make_pair("right", "右边"));	//更快速的匿名

make_pair()是为了让我们更加方便的使用键值对。

三,STl关联容器

  • STL关联容器有两种结构:树形结构,哈希结构;
  • 常见的树形结构关联容器有:mapsetmultimapmultiset;
  • 这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列

四,set 

set是按照一定次序存储元素的容器,其底层是一棵平衡二叉搜索树 (红黑树);

  由于二叉搜索树的每个节点的值满足左孩子 < 根 < 右孩子,并且二叉搜索树中没有重复的节点,所以set可以用来排序、去重和查找,同时由于这是一棵平衡树,所以 set查找的时间复杂度为 O(logN),效率非常高;

  • map等容器不同,set存储的元素其实只有value,在底层是<value, value>
  • 所以在插入元素时,只需要提供value即可,不需要构造键值对。使用set的迭代器进行遍历set时,会得到一个有序的序列。这是因为遍历的方式其实是二叉搜索树的中序遍历。
  • set中的元素也不允许修改,因为这可能会破坏搜索树的结构,但是 set允许插入和删除。

4.1模板参数 

class T:是 set 中存放元素的类型,实际在底层存储 <value, value> 的键值对 ;

class Compare = less<T>compare是仿函数,set中元素默认按照小于来比较(less);

class Alloc = allocator<T>:控制set的空间管理方式,使用STL提供的空间管理配置器.

 4.2默认构造

set::set - C++ Reference (cplusplus.com)

set提供了三种构造:默认空构造,迭代器区间构造和拷贝构造;

	set<int> s1;

	//迭代器区间构造
	s1 = { 1,2,3,4,5 };
	set<int> s2(s1.begin(),s1.end());

	//拷贝构造
	set<int> s3(s2);

 4.3使用

去重功能和自动排序

  • set具有降重的功能,当插入的数据已经存在时就不会再插入
  • 在打印set中的数据时,我们发现打印出来的结果是升序的,在二叉搜索树中,采用中序遍历的方式打印出来的结果就是升序的。

4.4增删查

insert

有三种插入方式,不建议用指定位置插入,有可能会破坏原二叉搜索结构

set::insert - C++ Reference (cplusplus.com)

find

 查找容器中是否存在某个特定的元素

  • 如果存在,返回该位置所在的迭代器;
  • 如果不存在,则返回set的结束位置的迭代器end()。

count 函数与其类似,只不过返回的是1和0,返回的是数字

set::count - C++ Reference (cplusplus.com)

 

erase

删除指定位置的值;set::erase - C++ Reference (cplusplus.com)

注:

  • 第二个重载函数,由于set中没有重复值,所以返回的值就是1。

  • 使用迭代器位置删除时,先find找到位置,再erase;
  •  使用指定数据的erase时,删除成功返回1,如果不存在则返回0。
  •  使用迭代器区间的erase时,将区间内的数据全部删除。

五,multiset 

成员函数和set一样,区别在于multiset支持数据重复,set不可以;

multiset - C++ Reference (cplusplus.com)

  • set:排序 + 降重
  • multiset:排序(不降重)

count

setcount成员函数和find功能类似,但是在multiset中它就有了它的作用。

multiset::count - C++ Reference (cplusplus.com)

erase

multiset::erase - C++ Reference (cplusplus.com)

 multiset中没有100,所以返回值就是0。 

find 

 multiset容器中存在重复元素,使用查找find函数,返回的是中序遍历中第一个元素;

multiset::find - C++ Reference (cplusplus.com)

其它函数基本上set一模一样了 

六,map 

map和set一样,也是一个关联式容器,底层是二叉搜索树。

map - C++ Reference (cplusplus.com)

  • 键值对映射 :map中的元素是一对键值对(pair),每一个key都有一个value对应。
  • 自动排序:与set类似,map中的元素在插入的同时会进行排序,默认情况下是key小的排在前面。
  • 唯一键:每个键在map中都是唯一的。
  • map查找的时间复杂度是O(logn).
  • 迭代器支持:支持使用迭代器来遍历map中的元素。本质上是搜索二叉树的中序遍历
  • 底层是用二叉搜索树(红黑树)实现的。
  • 可以使用[key]访问对应的value;(operator[])

6.1默认构造 

  • 使用默认构造函数创建的map是空的。
  •  插入键对值时,使用make_pair创建匿名对象更加方便。

特性 

  •  map具有降重功能,和set一样。
  •  不能插入相同的键值对,和set一样,map不支持重复数据。
  •  不能插入key值相同,val值不同的键值对。
  • map中虽然存放的是键值对,但是它的判断逻辑都只看key值,也就是first所表示的变量。
  • map也具有排序 + 降重的功能,依据只看key值而不管value值。

6.2使用 

insert

  • 参数类型 value_type pair<const key_type, mapped_type> 进行 typedef 得到的,也就是说参数是一个键值对,并使用引用传参
  • insert 第一个(框起来的)的返回类型为 pair<iterator, bool>
  • pair<iterator, bool>第一个iterator是迭代器;
    • 第二个是 bool,用于反映是否插入成功,成功返回true,否则返回false
  • 如果插入成功(即元素不存在进行插入):返回为 pair<iterator, true>;迭代器返回的是新插入元素位置的迭代器(pair
  • 如果插入失败(即元素已经存在):返回为 pair<iterator, false>,迭代器返回的是已存在元素位置的迭代器(pair);
  • map::insert - C++ Reference (cplusplus.com)

operator[]

通过键来访问或插入元素:

  • 如果键存在,它将返回对应的值的引用,即返回 Key 值的引用;
  • 如果键不存在,它会自动插入一个键值对,其中值部分为默认构造的值,并返回这个新插入值的引用。

:即使没有为元素分配映射值(即不给T传值,T为空),该元素使用其默认构造函数构造,插入新元素后,这会将容器大小增加1

map::operator[] - C++ Reference (cplusplus.com)

  • at 成员函数功能与 [] 类似,但是在具有键的元素存在时具有相同的行为(即查找的元素 Key 是存在的时候,at [] 的功能是一样的);
  • 但当不存在时抛出异常(即查找的 Key 不存在,[] 的功能是把这个 Key 直接插入map中,但是 at 是抛异常,不会进行插入);
示例

计算水果数量 

方法1

方法2 

总结

  • [] 的功能是 查找+修改+插入 (修改的原因是 [] 返回 mapped_type(T)值的引用)
  • at 的功能是 查找+修改(不支持插入)

 七,multimap

multimapmap的不同就是:

  • map中的key是唯一的,而multimapkey是可以重复的

multimap中没有重载 operator[] 操作,因为 multimapkey可能不唯一;(multimap不支持[key]操作);

使用时与map包含的头文件相同

 

<think>好的,我现在需要帮助用户理解C++标准库中mapmultimap的区别及使用场景。用户之前已经提问过关于multimapmultiset的区别,现在想更深入地比较mapmultimap。根据用户提供的引用内容,我需要综合这些信息,并确保回答结构清晰,符合用户的要求。 首先,回顾用户之前的问题和我的回答,已经讨论了multimapmultiset的底层实现都是红黑树,允许重复元素,而mapset不允许。现在用户的问题更聚焦于mapmultimap之间的比较,因此需要详细说明这两者的区别。 接下来,查看用户提供的引用内容。引用[3]明确指出mapmultimap的主要区别在于是否允许重复的键。引用[1]和[2]中的代码示例展示了multimap在处理相同键时的应用场景,比如统计单词频率并按频率排序,同时保留相同频率的单词。引用[5]的代码示例显示了multimap如何存储多个相同优先级的任务。引用[4]讨论了map与hash_map的区别,但用户的问题不涉及hash_map,所以可能暂时忽略这部分。 需要整理出以下几个方面的对比: 1. **键的唯一性**:map的键必须唯一,multimap允许重复键。 2. **数据结构**:两者都基于红黑树,保持有序。 3. **操作差异**:插入元素时,map的insert可能失败(如果键已存在),而multimap总是成功。访问元素时,map可以通过operator[]访问值,而multimap需要范围查询。 4. **使用场景**:map适合需要唯一键且快速查找的情况,如配置项;multimap适合需要多个相同键的情况,如事件调度、频率统计。 然后需要确保回答符合用户给出的格式要求,包括使用中文、行内公式用$...$,独立公式用$$...$$,但这里可能不需要数学公式。同时,要生成相关问题,并在引用处添加标识符。 需要注意用户之前的回答中提到的红黑树实现和性能分析,这里可以再次提及,但重点放在mapmultimap的比较上。此外,用户提供的代码示例可以用于用法示例部分,以展示具体差异。 最后,生成的相关问题需要围绕mapmultimap的底层实现、操作复杂度、实际应用等,帮助用户进一步理解。 现在,将这些点组织成一个结构清晰的回答,确保每个部分简明扼要,对比表格清晰,示例代码正确,并正确引用提供的资料。</think>#### C++中`map`与`multimap`的区别及使用场景详解 --- ### 一、核心区别对比 | **特性** | **`std::map`** | **`std::multimap`** | |---------------------|-----------------------------------|-----------------------------------| | **键唯一性** | 键必须唯一 | 允许重复键 | | **插入行为** | 插入重复键时失败(需手动判断) | 直接插入成功 | | **访问方式** | 支持`operator[]`按键快速访问值 | 不支持`operator[]`,需通过迭代器或范围查询 | | **典型操作复杂度** | 插入/删除/查找均为$O(\log n)$ | 同`map` | | **底层实现** | 红黑树(有序存储)[^3] | 红黑树(有序存储)[^3] | --- ### 二、具体差异解析 #### 1. **键的唯一性与插入行为** - **`map`**:每个键唯一,插入重复键时会覆盖原有值(或需通过`insert`返回值判断是否成功)。 ```cpp map<string, int> m; m["apple"] = 3; // 插入成功 m["apple"] = 5; // 覆盖原有值,m["apple"]=5 ``` - **`multimap`**:允许重复键,插入操作总是成功。 ```cpp multimap<string, int> mm; mm.insert({"apple", 3}); mm.insert({"apple", 5}); // 存储两个键为"apple"的键值对 ``` #### 2. **数据访问方式** - **`map`**:通过键直接访问值(`m[key]`),适合需要快速查找的场景。 ```cpp if (m.find("apple") != m.end()) cout << m["apple"]; // 输出5 ``` - **`multimap`**:需通过迭代器或`equal_range`获取相同键的所有值。 ```cpp auto range = mm.equal_range("apple"); for (auto it = range.first; it != range.second; ++it) cout << it->second << " "; // 输出3 5 ``` #### 3. **内存与性能** - **内存占用**:`multimap`因存储重复键可能占用更多内存。 - **查询效率**:两者查找复杂度均为$O(\log n)$,但`multimap`需处理多个相同键的遍历。 --- ### 三、典型应用场景 #### 1. **`map`的使用场景** - **键值唯一映射**:如用户ID到用户信息的存储。 - **快速查找与更新**:配置项管理、缓存系统。 ```cpp map<int, string> userDB; userDB[101] = "Alice"; // 用户ID唯一 ``` #### 2. **`multimap`的使用场景** - **一对多关系**:如事件调度(同一时间点多个事件)[^5]。 - **分组统计**:按频率排序单词(允许相同频率的单词)[^1][^2]。 ```cpp // 统计高频单词(引用[1]示例) map<string, int> wordCount; // ...统计单词频率 multimap<int, string, greater<int>> sortedByFreq; for (auto& e : wordCount) sortedByFreq.insert({e.second, e.first}); // 允许相同频率 ``` --- ### 四、选择依据 - **是否需要重复键**:是 → `multimap`;否 → `map`。 - **是否需要直接按键访问**:是 → `map`;否 → `multimap`。 - **是否需要有序遍历**:两者均支持有序遍历(基于红黑树)[^3]。 --- ### 五、代码示例对比 #### `map`的键唯一性 ```cpp map<int, string> taskMap; taskMap[2] = "Task 1"; taskMap[2] = "Task 2"; // 覆盖原有值,键2对应"Task 2" ``` #### `multimap`的重复键支持 ```cpp multimap<int, string> taskMultiMap; taskMultiMap.insert({2, "Task 1"}); taskMultiMap.insert({2, "Task 2"}); // 存储两个键为2的键值对 ``` --- ### 六、相关问题 1. **`map`的`operator[]`与`insert`方法有何区别?** - `operator[]`会覆盖已有键的值,`insert`返回插入结果(包含是否成功的标志)[^3]。 2. **如何高效遍历`multimap`中的所有重复键?** - 使用`equal_range`获取迭代器范围,避免线性搜索[^1][^5]。 3. **`multimap`与`unordered_multimap`的性能差异?** - `unordered_multimap`基于哈希表,查询平均$O(1)$,但无序且可能哈希冲突;`multimap`保持有序但查询为$O(\log n)$[^4]。 --- 通过上述对比,可以清晰理解`map`与`multimap`的核心差异及适用场景,从而在开发中合理选择容器类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值